roojs-ui.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @static
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         },
672                 /**
673                  * Find the current bootstrap width Grid size
674                  * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
675                  * @returns {String} (xs|sm|md|lg|xl)
676                  */
677                 
678                 getGridSize : function()
679                 {
680                         var w = Roo.lib.Dom.getViewWidth();
681                         switch(true) {
682                                 case w > 1200:
683                                         return 'xl';
684                                 case w > 992:
685                                         return 'lg';
686                                 case w > 768:
687                                         return 'md';
688                                 case w > 576:
689                                         return 'sm';
690                                 default:
691                                         return 'xs'
692                         }
693                         
694                 }
695         
696     });
697
698
699 })();
700
701 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
702                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
703                 "Roo.app", "Roo.ux" 
704                );
705 /*
706  * Based on:
707  * Ext JS Library 1.1.1
708  * Copyright(c) 2006-2007, Ext JS, LLC.
709  *
710  * Originally Released Under LGPL - original licence link has changed is not relivant.
711  *
712  * Fork - LGPL
713  * <script type="text/javascript">
714  */
715
716 (function() {    
717     // wrappedn so fnCleanup is not in global scope...
718     if(Roo.isIE) {
719         function fnCleanUp() {
720             var p = Function.prototype;
721             delete p.createSequence;
722             delete p.defer;
723             delete p.createDelegate;
724             delete p.createCallback;
725             delete p.createInterceptor;
726
727             window.detachEvent("onunload", fnCleanUp);
728         }
729         window.attachEvent("onunload", fnCleanUp);
730     }
731 })();
732
733
734 /**
735  * @class Function
736  * These functions are available on every Function object (any JavaScript function).
737  */
738 Roo.apply(Function.prototype, {
739      /**
740      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
741      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
742      * Will create a function that is bound to those 2 args.
743      * @return {Function} The new function
744     */
745     createCallback : function(/*args...*/){
746         // make args available, in function below
747         var args = arguments;
748         var method = this;
749         return function() {
750             return method.apply(window, args);
751         };
752     },
753
754     /**
755      * Creates a delegate (callback) that sets the scope to obj.
756      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
757      * Will create a function that is automatically scoped to this.
758      * @param {Object} obj (optional) The object for which the scope is set
759      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
760      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
761      *                                             if a number the args are inserted at the specified position
762      * @return {Function} The new function
763      */
764     createDelegate : function(obj, args, appendArgs){
765         var method = this;
766         return function() {
767             var callArgs = args || arguments;
768             if(appendArgs === true){
769                 callArgs = Array.prototype.slice.call(arguments, 0);
770                 callArgs = callArgs.concat(args);
771             }else if(typeof appendArgs == "number"){
772                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
773                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
774                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
775             }
776             return method.apply(obj || window, callArgs);
777         };
778     },
779
780     /**
781      * Calls this function after the number of millseconds specified.
782      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
783      * @param {Object} obj (optional) The object for which the scope is set
784      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
785      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
786      *                                             if a number the args are inserted at the specified position
787      * @return {Number} The timeout id that can be used with clearTimeout
788      */
789     defer : function(millis, obj, args, appendArgs){
790         var fn = this.createDelegate(obj, args, appendArgs);
791         if(millis){
792             return setTimeout(fn, millis);
793         }
794         fn();
795         return 0;
796     },
797     /**
798      * Create a combined function call sequence of the original function + the passed function.
799      * The resulting function returns the results of the original function.
800      * The passed fcn is called with the parameters of the original function
801      * @param {Function} fcn The function to sequence
802      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
803      * @return {Function} The new function
804      */
805     createSequence : function(fcn, scope){
806         if(typeof fcn != "function"){
807             return this;
808         }
809         var method = this;
810         return function() {
811             var retval = method.apply(this || window, arguments);
812             fcn.apply(scope || this || window, arguments);
813             return retval;
814         };
815     },
816
817     /**
818      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
819      * The resulting function returns the results of the original function.
820      * The passed fcn is called with the parameters of the original function.
821      * @addon
822      * @param {Function} fcn The function to call before the original
823      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
824      * @return {Function} The new function
825      */
826     createInterceptor : function(fcn, scope){
827         if(typeof fcn != "function"){
828             return this;
829         }
830         var method = this;
831         return function() {
832             fcn.target = this;
833             fcn.method = method;
834             if(fcn.apply(scope || this || window, arguments) === false){
835                 return;
836             }
837             return method.apply(this || window, arguments);
838         };
839     }
840 });
841 /*
842  * Based on:
843  * Ext JS Library 1.1.1
844  * Copyright(c) 2006-2007, Ext JS, LLC.
845  *
846  * Originally Released Under LGPL - original licence link has changed is not relivant.
847  *
848  * Fork - LGPL
849  * <script type="text/javascript">
850  */
851
852 Roo.applyIf(String, {
853     
854     /** @scope String */
855     
856     /**
857      * Escapes the passed string for ' and \
858      * @param {String} string The string to escape
859      * @return {String} The escaped string
860      * @static
861      */
862     escape : function(string) {
863         return string.replace(/('|\\)/g, "\\$1");
864     },
865
866     /**
867      * Pads the left side of a string with a specified character.  This is especially useful
868      * for normalizing number and date strings.  Example usage:
869      * <pre><code>
870 var s = String.leftPad('123', 5, '0');
871 // s now contains the string: '00123'
872 </code></pre>
873      * @param {String} string The original string
874      * @param {Number} size The total length of the output string
875      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
876      * @return {String} The padded string
877      * @static
878      */
879     leftPad : function (val, size, ch) {
880         var result = new String(val);
881         if(ch === null || ch === undefined || ch === '') {
882             ch = " ";
883         }
884         while (result.length < size) {
885             result = ch + result;
886         }
887         return result;
888     },
889
890     /**
891      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
892      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
893      * <pre><code>
894 var cls = 'my-class', text = 'Some text';
895 var s = String.format('<div class="{0}">{1}</div>', cls, text);
896 // s now contains the string: '<div class="my-class">Some text</div>'
897 </code></pre>
898      * @param {String} string The tokenized string to be formatted
899      * @param {String} value1 The value to replace token {0}
900      * @param {String} value2 Etc...
901      * @return {String} The formatted string
902      * @static
903      */
904     format : function(format){
905         var args = Array.prototype.slice.call(arguments, 1);
906         return format.replace(/\{(\d+)\}/g, function(m, i){
907             return Roo.util.Format.htmlEncode(args[i]);
908         });
909     }
910   
911     
912 });
913
914 /**
915  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
916  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
917  * they are already different, the first value passed in is returned.  Note that this method returns the new value
918  * but does not change the current string.
919  * <pre><code>
920 // alternate sort directions
921 sort = sort.toggle('ASC', 'DESC');
922
923 // instead of conditional logic:
924 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
925 </code></pre>
926  * @param {String} value The value to compare to the current string
927  * @param {String} other The new value to use if the string already equals the first value passed in
928  * @return {String} The new value
929  */
930  
931 String.prototype.toggle = function(value, other){
932     return this == value ? other : value;
933 };
934
935
936 /**
937   * Remove invalid unicode characters from a string 
938   *
939   * @return {String} The clean string
940   */
941 String.prototype.unicodeClean = function () {
942     return this.replace(/[\s\S]/g,
943         function(character) {
944             if (character.charCodeAt()< 256) {
945               return character;
946            }
947            try {
948                 encodeURIComponent(character);
949            } catch(e) { 
950               return '';
951            }
952            return character;
953         }
954     );
955 };
956   
957 /*
958  * Based on:
959  * Ext JS Library 1.1.1
960  * Copyright(c) 2006-2007, Ext JS, LLC.
961  *
962  * Originally Released Under LGPL - original licence link has changed is not relivant.
963  *
964  * Fork - LGPL
965  * <script type="text/javascript">
966  */
967
968  /**
969  * @class Number
970  */
971 Roo.applyIf(Number.prototype, {
972     /**
973      * Checks whether or not the current number is within a desired range.  If the number is already within the
974      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
975      * exceeded.  Note that this method returns the constrained value but does not change the current number.
976      * @param {Number} min The minimum number in the range
977      * @param {Number} max The maximum number in the range
978      * @return {Number} The constrained value if outside the range, otherwise the current value
979      */
980     constrain : function(min, max){
981         return Math.min(Math.max(this, min), max);
982     }
983 });/*
984  * Based on:
985  * Ext JS Library 1.1.1
986  * Copyright(c) 2006-2007, Ext JS, LLC.
987  *
988  * Originally Released Under LGPL - original licence link has changed is not relivant.
989  *
990  * Fork - LGPL
991  * <script type="text/javascript">
992  */
993  /**
994  * @class Array
995  */
996 Roo.applyIf(Array.prototype, {
997     /**
998      * 
999      * Checks whether or not the specified object exists in the array.
1000      * @param {Object} o The object to check for
1001      * @return {Number} The index of o in the array (or -1 if it is not found)
1002      */
1003     indexOf : function(o){
1004        for (var i = 0, len = this.length; i < len; i++){
1005               if(this[i] == o) { return i; }
1006        }
1007            return -1;
1008     },
1009
1010     /**
1011      * Removes the specified object from the array.  If the object is not found nothing happens.
1012      * @param {Object} o The object to remove
1013      */
1014     remove : function(o){
1015        var index = this.indexOf(o);
1016        if(index != -1){
1017            this.splice(index, 1);
1018        }
1019     },
1020     /**
1021      * Map (JS 1.6 compatibility)
1022      * @param {Function} function  to call
1023      */
1024     map : function(fun )
1025     {
1026         var len = this.length >>> 0;
1027         if (typeof fun != "function") {
1028             throw new TypeError();
1029         }
1030         var res = new Array(len);
1031         var thisp = arguments[1];
1032         for (var i = 0; i < len; i++)
1033         {
1034             if (i in this) {
1035                 res[i] = fun.call(thisp, this[i], i, this);
1036             }
1037         }
1038
1039         return res;
1040     },
1041     /**
1042      * equals
1043      * @param {Array} o The array to compare to
1044      * @returns {Boolean} true if the same
1045      */
1046     equals : function(b)
1047     {
1048             // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1049         if (this === b) {
1050             return true;
1051         }
1052         if (b == null) {
1053             return false;
1054         }
1055         if (this.length !== b.length) {
1056             return false;
1057         }
1058           
1059         // sort?? a.sort().equals(b.sort());
1060           
1061         for (var i = 0; i < this.length; ++i) {
1062             if (this[i] !== b[i]) {
1063             return false;
1064             }
1065         }
1066         return true;
1067     } 
1068     
1069     
1070     
1071     
1072 });
1073
1074 Roo.applyIf(Array, {
1075  /**
1076      * from
1077      * @static
1078      * @param {Array} o Or Array like object (eg. nodelist)
1079      * @returns {Array} 
1080      */
1081     from : function(o)
1082     {
1083         var ret= [];
1084     
1085         for (var i =0; i < o.length; i++) { 
1086             ret[i] = o[i];
1087         }
1088         return ret;
1089       
1090     }
1091 });
1092 /*
1093  * Based on:
1094  * Ext JS Library 1.1.1
1095  * Copyright(c) 2006-2007, Ext JS, LLC.
1096  *
1097  * Originally Released Under LGPL - original licence link has changed is not relivant.
1098  *
1099  * Fork - LGPL
1100  * <script type="text/javascript">
1101  */
1102
1103 /**
1104  * @class Date
1105  *
1106  * The date parsing and format syntax is a subset of
1107  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1108  * supported will provide results equivalent to their PHP versions.
1109  *
1110  * Following is the list of all currently supported formats:
1111  *<pre>
1112 Sample date:
1113 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1114
1115 Format  Output      Description
1116 ------  ----------  --------------------------------------------------------------
1117   d      10         Day of the month, 2 digits with leading zeros
1118   D      Wed        A textual representation of a day, three letters
1119   j      10         Day of the month without leading zeros
1120   l      Wednesday  A full textual representation of the day of the week
1121   S      th         English ordinal day of month suffix, 2 chars (use with j)
1122   w      3          Numeric representation of the day of the week
1123   z      9          The julian date, or day of the year (0-365)
1124   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1125   F      January    A full textual representation of the month
1126   m      01         Numeric representation of a month, with leading zeros
1127   M      Jan        Month name abbreviation, three letters
1128   n      1          Numeric representation of a month, without leading zeros
1129   t      31         Number of days in the given month
1130   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1131   Y      2007       A full numeric representation of a year, 4 digits
1132   y      07         A two digit representation of a year
1133   a      pm         Lowercase Ante meridiem and Post meridiem
1134   A      PM         Uppercase Ante meridiem and Post meridiem
1135   g      3          12-hour format of an hour without leading zeros
1136   G      15         24-hour format of an hour without leading zeros
1137   h      03         12-hour format of an hour with leading zeros
1138   H      15         24-hour format of an hour with leading zeros
1139   i      05         Minutes with leading zeros
1140   s      01         Seconds, with leading zeros
1141   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1142   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1143   T      CST        Timezone setting of the machine running the code
1144   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1145 </pre>
1146  *
1147  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1148  * <pre><code>
1149 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1150 document.write(dt.format('Y-m-d'));                         //2007-01-10
1151 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1152 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1153  </code></pre>
1154  *
1155  * Here are some standard date/time patterns that you might find helpful.  They
1156  * are not part of the source of Date.js, but to use them you can simply copy this
1157  * block of code into any script that is included after Date.js and they will also become
1158  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1159  * <pre><code>
1160 Date.patterns = {
1161     ISO8601Long:"Y-m-d H:i:s",
1162     ISO8601Short:"Y-m-d",
1163     ShortDate: "n/j/Y",
1164     LongDate: "l, F d, Y",
1165     FullDateTime: "l, F d, Y g:i:s A",
1166     MonthDay: "F d",
1167     ShortTime: "g:i A",
1168     LongTime: "g:i:s A",
1169     SortableDateTime: "Y-m-d\\TH:i:s",
1170     UniversalSortableDateTime: "Y-m-d H:i:sO",
1171     YearMonth: "F, Y"
1172 };
1173 </code></pre>
1174  *
1175  * Example usage:
1176  * <pre><code>
1177 var dt = new Date();
1178 document.write(dt.format(Date.patterns.ShortDate));
1179  </code></pre>
1180  */
1181
1182 /*
1183  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1184  * They generate precompiled functions from date formats instead of parsing and
1185  * processing the pattern every time you format a date.  These functions are available
1186  * on every Date object (any javascript function).
1187  *
1188  * The original article and download are here:
1189  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1190  *
1191  */
1192  
1193  
1194  // was in core
1195 /**
1196  Returns the number of milliseconds between this date and date
1197  @param {Date} date (optional) Defaults to now
1198  @return {Number} The diff in milliseconds
1199  @member Date getElapsed
1200  */
1201 Date.prototype.getElapsed = function(date) {
1202         return Math.abs((date || new Date()).getTime()-this.getTime());
1203 };
1204 // was in date file..
1205
1206
1207 // private
1208 Date.parseFunctions = {count:0};
1209 // private
1210 Date.parseRegexes = [];
1211 // private
1212 Date.formatFunctions = {count:0};
1213
1214 // private
1215 Date.prototype.dateFormat = function(format) {
1216     if (Date.formatFunctions[format] == null) {
1217         Date.createNewFormat(format);
1218     }
1219     var func = Date.formatFunctions[format];
1220     return this[func]();
1221 };
1222
1223
1224 /**
1225  * Formats a date given the supplied format string
1226  * @param {String} format The format string
1227  * @return {String} The formatted date
1228  * @method
1229  */
1230 Date.prototype.format = Date.prototype.dateFormat;
1231
1232 // private
1233 Date.createNewFormat = function(format) {
1234     var funcName = "format" + Date.formatFunctions.count++;
1235     Date.formatFunctions[format] = funcName;
1236     var code = "Date.prototype." + funcName + " = function(){return ";
1237     var special = false;
1238     var ch = '';
1239     for (var i = 0; i < format.length; ++i) {
1240         ch = format.charAt(i);
1241         if (!special && ch == "\\") {
1242             special = true;
1243         }
1244         else if (special) {
1245             special = false;
1246             code += "'" + String.escape(ch) + "' + ";
1247         }
1248         else {
1249             code += Date.getFormatCode(ch);
1250         }
1251     }
1252     /** eval:var:zzzzzzzzzzzzz */
1253     eval(code.substring(0, code.length - 3) + ";}");
1254 };
1255
1256 // private
1257 Date.getFormatCode = function(character) {
1258     switch (character) {
1259     case "d":
1260         return "String.leftPad(this.getDate(), 2, '0') + ";
1261     case "D":
1262         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1263     case "j":
1264         return "this.getDate() + ";
1265     case "l":
1266         return "Date.dayNames[this.getDay()] + ";
1267     case "S":
1268         return "this.getSuffix() + ";
1269     case "w":
1270         return "this.getDay() + ";
1271     case "z":
1272         return "this.getDayOfYear() + ";
1273     case "W":
1274         return "this.getWeekOfYear() + ";
1275     case "F":
1276         return "Date.monthNames[this.getMonth()] + ";
1277     case "m":
1278         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1279     case "M":
1280         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1281     case "n":
1282         return "(this.getMonth() + 1) + ";
1283     case "t":
1284         return "this.getDaysInMonth() + ";
1285     case "L":
1286         return "(this.isLeapYear() ? 1 : 0) + ";
1287     case "Y":
1288         return "this.getFullYear() + ";
1289     case "y":
1290         return "('' + this.getFullYear()).substring(2, 4) + ";
1291     case "a":
1292         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1293     case "A":
1294         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1295     case "g":
1296         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1297     case "G":
1298         return "this.getHours() + ";
1299     case "h":
1300         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1301     case "H":
1302         return "String.leftPad(this.getHours(), 2, '0') + ";
1303     case "i":
1304         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1305     case "s":
1306         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1307     case "O":
1308         return "this.getGMTOffset() + ";
1309     case "P":
1310         return "this.getGMTColonOffset() + ";
1311     case "T":
1312         return "this.getTimezone() + ";
1313     case "Z":
1314         return "(this.getTimezoneOffset() * -60) + ";
1315     default:
1316         return "'" + String.escape(character) + "' + ";
1317     }
1318 };
1319
1320 /**
1321  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1322  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1323  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1324  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1325  * string or the parse operation will fail.
1326  * Example Usage:
1327 <pre><code>
1328 //dt = Fri May 25 2007 (current date)
1329 var dt = new Date();
1330
1331 //dt = Thu May 25 2006 (today's month/day in 2006)
1332 dt = Date.parseDate("2006", "Y");
1333
1334 //dt = Sun Jan 15 2006 (all date parts specified)
1335 dt = Date.parseDate("2006-1-15", "Y-m-d");
1336
1337 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1338 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1339 </code></pre>
1340  * @param {String} input The unparsed date as a string
1341  * @param {String} format The format the date is in
1342  * @return {Date} The parsed date
1343  * @static
1344  */
1345 Date.parseDate = function(input, format) {
1346     if (Date.parseFunctions[format] == null) {
1347         Date.createParser(format);
1348     }
1349     var func = Date.parseFunctions[format];
1350     return Date[func](input);
1351 };
1352 /**
1353  * @private
1354  */
1355
1356 Date.createParser = function(format) {
1357     var funcName = "parse" + Date.parseFunctions.count++;
1358     var regexNum = Date.parseRegexes.length;
1359     var currentGroup = 1;
1360     Date.parseFunctions[format] = funcName;
1361
1362     var code = "Date." + funcName + " = function(input){\n"
1363         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1364         + "var d = new Date();\n"
1365         + "y = d.getFullYear();\n"
1366         + "m = d.getMonth();\n"
1367         + "d = d.getDate();\n"
1368         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1369         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1370         + "if (results && results.length > 0) {";
1371     var regex = "";
1372
1373     var special = false;
1374     var ch = '';
1375     for (var i = 0; i < format.length; ++i) {
1376         ch = format.charAt(i);
1377         if (!special && ch == "\\") {
1378             special = true;
1379         }
1380         else if (special) {
1381             special = false;
1382             regex += String.escape(ch);
1383         }
1384         else {
1385             var obj = Date.formatCodeToRegex(ch, currentGroup);
1386             currentGroup += obj.g;
1387             regex += obj.s;
1388             if (obj.g && obj.c) {
1389                 code += obj.c;
1390             }
1391         }
1392     }
1393
1394     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1395         + "{v = new Date(y, m, d, h, i, s); v.setFullYear(y);}\n"
1396         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1397         + "{v = new Date(y, m, d, h, i); v.setFullYear(y);}\n"
1398         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1399         + "{v = new Date(y, m, d, h); v.setFullYear(y);}\n"
1400         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1401         + "{v = new Date(y, m, d); v.setFullYear(y);}\n"
1402         + "else if (y >= 0 && m >= 0)\n"
1403         + "{v = new Date(y, m); v.setFullYear(y);}\n"
1404         + "else if (y >= 0)\n"
1405         + "{v = new Date(y); v.setFullYear(y);}\n"
1406         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1407         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1408         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1409         + ";}";
1410
1411     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1412     /** eval:var:zzzzzzzzzzzzz */
1413     eval(code);
1414 };
1415
1416 // private
1417 Date.formatCodeToRegex = function(character, currentGroup) {
1418     switch (character) {
1419     case "D":
1420         return {g:0,
1421         c:null,
1422         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1423     case "j":
1424         return {g:1,
1425             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1426             s:"(\\d{1,2})"}; // day of month without leading zeroes
1427     case "d":
1428         return {g:1,
1429             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1430             s:"(\\d{2})"}; // day of month with leading zeroes
1431     case "l":
1432         return {g:0,
1433             c:null,
1434             s:"(?:" + Date.dayNames.join("|") + ")"};
1435     case "S":
1436         return {g:0,
1437             c:null,
1438             s:"(?:st|nd|rd|th)"};
1439     case "w":
1440         return {g:0,
1441             c:null,
1442             s:"\\d"};
1443     case "z":
1444         return {g:0,
1445             c:null,
1446             s:"(?:\\d{1,3})"};
1447     case "W":
1448         return {g:0,
1449             c:null,
1450             s:"(?:\\d{2})"};
1451     case "F":
1452         return {g:1,
1453             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1454             s:"(" + Date.monthNames.join("|") + ")"};
1455     case "M":
1456         return {g:1,
1457             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1458             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1459     case "n":
1460         return {g:1,
1461             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1462             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1463     case "m":
1464         return {g:1,
1465             c:"m = Math.max(0,parseInt(results[" + currentGroup + "], 10) - 1);\n",
1466             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1467     case "t":
1468         return {g:0,
1469             c:null,
1470             s:"\\d{1,2}"};
1471     case "L":
1472         return {g:0,
1473             c:null,
1474             s:"(?:1|0)"};
1475     case "Y":
1476         return {g:1,
1477             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1478             s:"(\\d{4})"};
1479     case "y":
1480         return {g:1,
1481             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1482                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1483             s:"(\\d{1,2})"};
1484     case "a":
1485         return {g:1,
1486             c:"if (results[" + currentGroup + "] == 'am') {\n"
1487                 + "if (h == 12) { h = 0; }\n"
1488                 + "} else { if (h < 12) { h += 12; }}",
1489             s:"(am|pm)"};
1490     case "A":
1491         return {g:1,
1492             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1493                 + "if (h == 12) { h = 0; }\n"
1494                 + "} else { if (h < 12) { h += 12; }}",
1495             s:"(AM|PM)"};
1496     case "g":
1497     case "G":
1498         return {g:1,
1499             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1500             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1501     case "h":
1502     case "H":
1503         return {g:1,
1504             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1505             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1506     case "i":
1507         return {g:1,
1508             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1509             s:"(\\d{2})"};
1510     case "s":
1511         return {g:1,
1512             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1513             s:"(\\d{2})"};
1514     case "O":
1515         return {g:1,
1516             c:[
1517                 "o = results[", currentGroup, "];\n",
1518                 "var sn = o.substring(0,1);\n", // get + / - sign
1519                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1520                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1521                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1522                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1523             ].join(""),
1524             s:"([+\-]\\d{2,4})"};
1525     
1526     
1527     case "P":
1528         return {g:1,
1529                 c:[
1530                    "o = results[", currentGroup, "];\n",
1531                    "var sn = o.substring(0,1);\n",
1532                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1533                    "var mn = o.substring(4,6) % 60;\n",
1534                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1535                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1536             ].join(""),
1537             s:"([+\-]\\d{4})"};
1538     case "T":
1539         return {g:0,
1540             c:null,
1541             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1542     case "Z":
1543         return {g:1,
1544             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1545                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1546             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1547     default:
1548         return {g:0,
1549             c:null,
1550             s:String.escape(character)};
1551     }
1552 };
1553
1554 /**
1555  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1556  * @return {String} The abbreviated timezone name (e.g. 'CST')
1557  */
1558 Date.prototype.getTimezone = function() {
1559     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1560 };
1561
1562 /**
1563  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1564  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1565  */
1566 Date.prototype.getGMTOffset = function() {
1567     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1568         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1569         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1570 };
1571
1572 /**
1573  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1574  * @return {String} 2-characters representing hours and 2-characters representing minutes
1575  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1576  */
1577 Date.prototype.getGMTColonOffset = function() {
1578         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1579                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1580                 + ":"
1581                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1582 }
1583
1584 /**
1585  * Get the numeric day number of the year, adjusted for leap year.
1586  * @return {Number} 0 through 364 (365 in leap years)
1587  */
1588 Date.prototype.getDayOfYear = function() {
1589     var num = 0;
1590     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1591     for (var i = 0; i < this.getMonth(); ++i) {
1592         num += Date.daysInMonth[i];
1593     }
1594     return num + this.getDate() - 1;
1595 };
1596
1597 /**
1598  * Get the string representation of the numeric week number of the year
1599  * (equivalent to the format specifier 'W').
1600  * @return {String} '00' through '52'
1601  */
1602 Date.prototype.getWeekOfYear = function() {
1603     // Skip to Thursday of this week
1604     var now = this.getDayOfYear() + (4 - this.getDay());
1605     // Find the first Thursday of the year
1606     var jan1 = new Date(this.getFullYear(), 0, 1);
1607     var then = (7 - jan1.getDay() + 4);
1608     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1609 };
1610
1611 /**
1612  * Whether or not the current date is in a leap year.
1613  * @return {Boolean} True if the current date is in a leap year, else false
1614  */
1615 Date.prototype.isLeapYear = function() {
1616     var year = this.getFullYear();
1617     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1618 };
1619
1620 /**
1621  * Get the first day of the current month, adjusted for leap year.  The returned value
1622  * is the numeric day index within the week (0-6) which can be used in conjunction with
1623  * the {@link #monthNames} array to retrieve the textual day name.
1624  * Example:
1625  *<pre><code>
1626 var dt = new Date('1/10/2007');
1627 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1628 </code></pre>
1629  * @return {Number} The day number (0-6)
1630  */
1631 Date.prototype.getFirstDayOfMonth = function() {
1632     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1633     return (day < 0) ? (day + 7) : day;
1634 };
1635
1636 /**
1637  * Get the last day of the current month, adjusted for leap year.  The returned value
1638  * is the numeric day index within the week (0-6) which can be used in conjunction with
1639  * the {@link #monthNames} array to retrieve the textual day name.
1640  * Example:
1641  *<pre><code>
1642 var dt = new Date('1/10/2007');
1643 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1644 </code></pre>
1645  * @return {Number} The day number (0-6)
1646  */
1647 Date.prototype.getLastDayOfMonth = function() {
1648     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1649     return (day < 0) ? (day + 7) : day;
1650 };
1651
1652
1653 /**
1654  * Get the first date of this date's month
1655  * @return {Date}
1656  */
1657 Date.prototype.getFirstDateOfMonth = function() {
1658     return new Date(this.getFullYear(), this.getMonth(), 1);
1659 };
1660
1661 /**
1662  * Get the last date of this date's month
1663  * @return {Date}
1664  */
1665 Date.prototype.getLastDateOfMonth = function() {
1666     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1667 };
1668 /**
1669  * Get the number of days in the current month, adjusted for leap year.
1670  * @return {Number} The number of days in the month
1671  */
1672 Date.prototype.getDaysInMonth = function() {
1673     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1674     return Date.daysInMonth[this.getMonth()];
1675 };
1676
1677 /**
1678  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1679  * @return {String} 'st, 'nd', 'rd' or 'th'
1680  */
1681 Date.prototype.getSuffix = function() {
1682     switch (this.getDate()) {
1683         case 1:
1684         case 21:
1685         case 31:
1686             return "st";
1687         case 2:
1688         case 22:
1689             return "nd";
1690         case 3:
1691         case 23:
1692             return "rd";
1693         default:
1694             return "th";
1695     }
1696 };
1697
1698 // private
1699 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1700
1701 /**
1702  * An array of textual month names.
1703  * Override these values for international dates, for example...
1704  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1705  * @type Array
1706  * @static
1707  */
1708 Date.monthNames =
1709    ["January",
1710     "February",
1711     "March",
1712     "April",
1713     "May",
1714     "June",
1715     "July",
1716     "August",
1717     "September",
1718     "October",
1719     "November",
1720     "December"];
1721
1722 /**
1723  * An array of textual day names.
1724  * Override these values for international dates, for example...
1725  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1726  * @type Array
1727  * @static
1728  */
1729 Date.dayNames =
1730    ["Sunday",
1731     "Monday",
1732     "Tuesday",
1733     "Wednesday",
1734     "Thursday",
1735     "Friday",
1736     "Saturday"];
1737
1738 // private
1739 Date.y2kYear = 50;
1740 // private
1741 Date.monthNumbers = {
1742     Jan:0,
1743     Feb:1,
1744     Mar:2,
1745     Apr:3,
1746     May:4,
1747     Jun:5,
1748     Jul:6,
1749     Aug:7,
1750     Sep:8,
1751     Oct:9,
1752     Nov:10,
1753     Dec:11};
1754
1755 /**
1756  * Creates and returns a new Date instance with the exact same date value as the called instance.
1757  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1758  * variable will also be changed.  When the intention is to create a new variable that will not
1759  * modify the original instance, you should create a clone.
1760  *
1761  * Example of correctly cloning a date:
1762  * <pre><code>
1763 //wrong way:
1764 var orig = new Date('10/1/2006');
1765 var copy = orig;
1766 copy.setDate(5);
1767 document.write(orig);  //returns 'Thu Oct 05 2006'!
1768
1769 //correct way:
1770 var orig = new Date('10/1/2006');
1771 var copy = orig.clone();
1772 copy.setDate(5);
1773 document.write(orig);  //returns 'Thu Oct 01 2006'
1774 </code></pre>
1775  * @return {Date} The new Date instance
1776  */
1777 Date.prototype.clone = function() {
1778         return new Date(this.getTime());
1779 };
1780
1781 /**
1782  * Clears any time information from this date
1783  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1784  @return {Date} this or the clone
1785  */
1786 Date.prototype.clearTime = function(clone){
1787     if(clone){
1788         return this.clone().clearTime();
1789     }
1790     this.setHours(0);
1791     this.setMinutes(0);
1792     this.setSeconds(0);
1793     this.setMilliseconds(0);
1794     return this;
1795 };
1796
1797 // private
1798 // safari setMonth is broken -- check that this is only donw once...
1799 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1800     Date.brokenSetMonth = Date.prototype.setMonth;
1801         Date.prototype.setMonth = function(num){
1802                 if(num <= -1){
1803                         var n = Math.ceil(-num);
1804                         var back_year = Math.ceil(n/12);
1805                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1806                         this.setFullYear(this.getFullYear() - back_year);
1807                         return Date.brokenSetMonth.call(this, month);
1808                 } else {
1809                         return Date.brokenSetMonth.apply(this, arguments);
1810                 }
1811         };
1812 }
1813
1814 /** Date interval constant 
1815 * @static 
1816 * @type String */
1817 Date.MILLI = "ms";
1818 /** Date interval constant 
1819 * @static 
1820 * @type String */
1821 Date.SECOND = "s";
1822 /** Date interval constant 
1823 * @static 
1824 * @type String */
1825 Date.MINUTE = "mi";
1826 /** Date interval constant 
1827 * @static 
1828 * @type String */
1829 Date.HOUR = "h";
1830 /** Date interval constant 
1831 * @static 
1832 * @type String */
1833 Date.DAY = "d";
1834 /** Date interval constant 
1835 * @static 
1836 * @type String */
1837 Date.MONTH = "mo";
1838 /** Date interval constant 
1839 * @static 
1840 * @type String */
1841 Date.YEAR = "y";
1842
1843 /**
1844  * Provides a convenient method of performing basic date arithmetic.  This method
1845  * does not modify the Date instance being called - it creates and returns
1846  * a new Date instance containing the resulting date value.
1847  *
1848  * Examples:
1849  * <pre><code>
1850 //Basic usage:
1851 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1852 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1853
1854 //Negative values will subtract correctly:
1855 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1856 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1857
1858 //You can even chain several calls together in one line!
1859 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1860 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1861  </code></pre>
1862  *
1863  * @param {String} interval   A valid date interval enum value
1864  * @param {Number} value      The amount to add to the current date
1865  * @return {Date} The new Date instance
1866  */
1867 Date.prototype.add = function(interval, value){
1868   var d = this.clone();
1869   if (!interval || value === 0) { return d; }
1870   switch(interval.toLowerCase()){
1871     case Date.MILLI:
1872       d.setMilliseconds(this.getMilliseconds() + value);
1873       break;
1874     case Date.SECOND:
1875       d.setSeconds(this.getSeconds() + value);
1876       break;
1877     case Date.MINUTE:
1878       d.setMinutes(this.getMinutes() + value);
1879       break;
1880     case Date.HOUR:
1881       d.setHours(this.getHours() + value);
1882       break;
1883     case Date.DAY:
1884       d.setDate(this.getDate() + value);
1885       break;
1886     case Date.MONTH:
1887       var day = this.getDate();
1888       if(day > 28){
1889           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1890       }
1891       d.setDate(day);
1892       d.setMonth(this.getMonth() + value);
1893       break;
1894     case Date.YEAR:
1895       d.setFullYear(this.getFullYear() + value);
1896       break;
1897   }
1898   return d;
1899 };
1900 /**
1901  * @class Roo.lib.Dom
1902  * @licence LGPL
1903  * @static
1904  * 
1905  * Dom utils (from YIU afaik)
1906  *
1907  * 
1908  **/
1909 Roo.lib.Dom = {
1910     /**
1911      * Get the view width
1912      * @param {Boolean} full True will get the full document, otherwise it's the view width
1913      * @return {Number} The width
1914      */
1915      
1916     getViewWidth : function(full) {
1917         return full ? this.getDocumentWidth() : this.getViewportWidth();
1918     },
1919     /**
1920      * Get the view height
1921      * @param {Boolean} full True will get the full document, otherwise it's the view height
1922      * @return {Number} The height
1923      */
1924     getViewHeight : function(full) {
1925         return full ? this.getDocumentHeight() : this.getViewportHeight();
1926     },
1927     /**
1928      * Get the Full Document height 
1929      * @return {Number} The height
1930      */
1931     getDocumentHeight: function() {
1932         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1933         return Math.max(scrollHeight, this.getViewportHeight());
1934     },
1935     /**
1936      * Get the Full Document width
1937      * @return {Number} The width
1938      */
1939     getDocumentWidth: function() {
1940         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1941         return Math.max(scrollWidth, this.getViewportWidth());
1942     },
1943     /**
1944      * Get the Window Viewport height
1945      * @return {Number} The height
1946      */
1947     getViewportHeight: function() {
1948         var height = self.innerHeight;
1949         var mode = document.compatMode;
1950
1951         if ((mode || Roo.isIE) && !Roo.isOpera) {
1952             height = (mode == "CSS1Compat") ?
1953                      document.documentElement.clientHeight :
1954                      document.body.clientHeight;
1955         }
1956
1957         return height;
1958     },
1959     /**
1960      * Get the Window Viewport width
1961      * @return {Number} The width
1962      */
1963     getViewportWidth: function() {
1964         var width = self.innerWidth;
1965         var mode = document.compatMode;
1966
1967         if (mode || Roo.isIE) {
1968             width = (mode == "CSS1Compat") ?
1969                     document.documentElement.clientWidth :
1970                     document.body.clientWidth;
1971         }
1972         return width;
1973     },
1974
1975     isAncestor : function(p, c) {
1976         p = Roo.getDom(p);
1977         c = Roo.getDom(c);
1978         if (!p || !c) {
1979             return false;
1980         }
1981
1982         if (p.contains && !Roo.isSafari) {
1983             return p.contains(c);
1984         } else if (p.compareDocumentPosition) {
1985             return !!(p.compareDocumentPosition(c) & 16);
1986         } else {
1987             var parent = c.parentNode;
1988             while (parent) {
1989                 if (parent == p) {
1990                     return true;
1991                 }
1992                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1993                     return false;
1994                 }
1995                 parent = parent.parentNode;
1996             }
1997             return false;
1998         }
1999     },
2000
2001     getRegion : function(el) {
2002         return Roo.lib.Region.getRegion(el);
2003     },
2004
2005     getY : function(el) {
2006         return this.getXY(el)[1];
2007     },
2008
2009     getX : function(el) {
2010         return this.getXY(el)[0];
2011     },
2012
2013     getXY : function(el) {
2014         var p, pe, b, scroll, bd = document.body;
2015         el = Roo.getDom(el);
2016         var fly = Roo.lib.AnimBase.fly;
2017         if (el.getBoundingClientRect) {
2018             b = el.getBoundingClientRect();
2019             scroll = fly(document).getScroll();
2020             return [b.left + scroll.left, b.top + scroll.top];
2021         }
2022         var x = 0, y = 0;
2023
2024         p = el;
2025
2026         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2027
2028         while (p) {
2029
2030             x += p.offsetLeft;
2031             y += p.offsetTop;
2032
2033             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2034                 hasAbsolute = true;
2035             }
2036
2037             if (Roo.isGecko) {
2038                 pe = fly(p);
2039
2040                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2041                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2042
2043
2044                 x += bl;
2045                 y += bt;
2046
2047
2048                 if (p != el && pe.getStyle('overflow') != 'visible') {
2049                     x += bl;
2050                     y += bt;
2051                 }
2052             }
2053             p = p.offsetParent;
2054         }
2055
2056         if (Roo.isSafari && hasAbsolute) {
2057             x -= bd.offsetLeft;
2058             y -= bd.offsetTop;
2059         }
2060
2061         if (Roo.isGecko && !hasAbsolute) {
2062             var dbd = fly(bd);
2063             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2064             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2065         }
2066
2067         p = el.parentNode;
2068         while (p && p != bd) {
2069             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2070                 x -= p.scrollLeft;
2071                 y -= p.scrollTop;
2072             }
2073             p = p.parentNode;
2074         }
2075         return [x, y];
2076     },
2077  
2078   
2079
2080
2081     setXY : function(el, xy) {
2082         el = Roo.fly(el, '_setXY');
2083         el.position();
2084         var pts = el.translatePoints(xy);
2085         if (xy[0] !== false) {
2086             el.dom.style.left = pts.left + "px";
2087         }
2088         if (xy[1] !== false) {
2089             el.dom.style.top = pts.top + "px";
2090         }
2091     },
2092
2093     setX : function(el, x) {
2094         this.setXY(el, [x, false]);
2095     },
2096
2097     setY : function(el, y) {
2098         this.setXY(el, [false, y]);
2099     }
2100 };
2101 /*
2102  * Portions of this file are based on pieces of Yahoo User Interface Library
2103  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2104  * YUI licensed under the BSD License:
2105  * http://developer.yahoo.net/yui/license.txt
2106  * <script type="text/javascript">
2107  *
2108  */
2109
2110 Roo.lib.Event = function() {
2111     var loadComplete = false;
2112     var listeners = [];
2113     var unloadListeners = [];
2114     var retryCount = 0;
2115     var onAvailStack = [];
2116     var counter = 0;
2117     var lastError = null;
2118
2119     return {
2120         POLL_RETRYS: 200,
2121         POLL_INTERVAL: 20,
2122         EL: 0,
2123         TYPE: 1,
2124         FN: 2,
2125         WFN: 3,
2126         OBJ: 3,
2127         ADJ_SCOPE: 4,
2128         _interval: null,
2129
2130         startInterval: function() {
2131             if (!this._interval) {
2132                 var self = this;
2133                 var callback = function() {
2134                     self._tryPreloadAttach();
2135                 };
2136                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2137
2138             }
2139         },
2140
2141         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2142             onAvailStack.push({ id:         p_id,
2143                 fn:         p_fn,
2144                 obj:        p_obj,
2145                 override:   p_override,
2146                 checkReady: false    });
2147
2148             retryCount = this.POLL_RETRYS;
2149             this.startInterval();
2150         },
2151
2152
2153         addListener: function(el, eventName, fn) {
2154             el = Roo.getDom(el);
2155             if (!el || !fn) {
2156                 return false;
2157             }
2158
2159             if ("unload" == eventName) {
2160                 unloadListeners[unloadListeners.length] =
2161                 [el, eventName, fn];
2162                 return true;
2163             }
2164
2165             var wrappedFn = function(e) {
2166                 return fn(Roo.lib.Event.getEvent(e));
2167             };
2168
2169             var li = [el, eventName, fn, wrappedFn];
2170
2171             var index = listeners.length;
2172             listeners[index] = li;
2173
2174             this.doAdd(el, eventName, wrappedFn, false);
2175             return true;
2176
2177         },
2178
2179
2180         removeListener: function(el, eventName, fn) {
2181             var i, len;
2182
2183             el = Roo.getDom(el);
2184
2185             if(!fn) {
2186                 return this.purgeElement(el, false, eventName);
2187             }
2188
2189
2190             if ("unload" == eventName) {
2191
2192                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2193                     var li = unloadListeners[i];
2194                     if (li &&
2195                         li[0] == el &&
2196                         li[1] == eventName &&
2197                         li[2] == fn) {
2198                         unloadListeners.splice(i, 1);
2199                         return true;
2200                     }
2201                 }
2202
2203                 return false;
2204             }
2205
2206             var cacheItem = null;
2207
2208
2209             var index = arguments[3];
2210
2211             if ("undefined" == typeof index) {
2212                 index = this._getCacheIndex(el, eventName, fn);
2213             }
2214
2215             if (index >= 0) {
2216                 cacheItem = listeners[index];
2217             }
2218
2219             if (!el || !cacheItem) {
2220                 return false;
2221             }
2222
2223             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2224
2225             delete listeners[index][this.WFN];
2226             delete listeners[index][this.FN];
2227             listeners.splice(index, 1);
2228
2229             return true;
2230
2231         },
2232
2233
2234         getTarget: function(ev, resolveTextNode) {
2235             ev = ev.browserEvent || ev;
2236             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2237             var t = ev.target || ev.srcElement;
2238             return this.resolveTextNode(t);
2239         },
2240
2241
2242         resolveTextNode: function(node) {
2243             if (Roo.isSafari && node && 3 == node.nodeType) {
2244                 return node.parentNode;
2245             } else {
2246                 return node;
2247             }
2248         },
2249
2250
2251         getPageX: function(ev) {
2252             ev = ev.browserEvent || ev;
2253             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2254             var x = ev.pageX;
2255             if (!x && 0 !== x) {
2256                 x = ev.clientX || 0;
2257
2258                 if (Roo.isIE) {
2259                     x += this.getScroll()[1];
2260                 }
2261             }
2262
2263             return x;
2264         },
2265
2266
2267         getPageY: function(ev) {
2268             ev = ev.browserEvent || ev;
2269             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2270             var y = ev.pageY;
2271             if (!y && 0 !== y) {
2272                 y = ev.clientY || 0;
2273
2274                 if (Roo.isIE) {
2275                     y += this.getScroll()[0];
2276                 }
2277             }
2278
2279
2280             return y;
2281         },
2282
2283
2284         getXY: function(ev) {
2285             ev = ev.browserEvent || ev;
2286             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2287             return [this.getPageX(ev), this.getPageY(ev)];
2288         },
2289
2290
2291         getRelatedTarget: function(ev) {
2292             ev = ev.browserEvent || ev;
2293             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2294             var t = ev.relatedTarget;
2295             if (!t) {
2296                 if (ev.type == "mouseout") {
2297                     t = ev.toElement;
2298                 } else if (ev.type == "mouseover") {
2299                     t = ev.fromElement;
2300                 }
2301             }
2302
2303             return this.resolveTextNode(t);
2304         },
2305
2306
2307         getTime: function(ev) {
2308             ev = ev.browserEvent || ev;
2309             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2310             if (!ev.time) {
2311                 var t = new Date().getTime();
2312                 try {
2313                     ev.time = t;
2314                 } catch(ex) {
2315                     this.lastError = ex;
2316                     return t;
2317                 }
2318             }
2319
2320             return ev.time;
2321         },
2322
2323
2324         stopEvent: function(ev) {
2325             this.stopPropagation(ev);
2326             this.preventDefault(ev);
2327         },
2328
2329
2330         stopPropagation: function(ev) {
2331             ev = ev.browserEvent || ev;
2332             if (ev.stopPropagation) {
2333                 ev.stopPropagation();
2334             } else {
2335                 ev.cancelBubble = true;
2336             }
2337         },
2338
2339
2340         preventDefault: function(ev) {
2341             ev = ev.browserEvent || ev;
2342             if(ev.preventDefault) {
2343                 ev.preventDefault();
2344             } else {
2345                 ev.returnValue = false;
2346             }
2347         },
2348
2349
2350         getEvent: function(e) {
2351             var ev = e || window.event;
2352             if (!ev) {
2353                 var c = this.getEvent.caller;
2354                 while (c) {
2355                     ev = c.arguments[0];
2356                     if (ev && Event == ev.constructor) {
2357                         break;
2358                     }
2359                     c = c.caller;
2360                 }
2361             }
2362             return ev;
2363         },
2364
2365
2366         getCharCode: function(ev) {
2367             ev = ev.browserEvent || ev;
2368             return ev.charCode || ev.keyCode || 0;
2369         },
2370
2371
2372         _getCacheIndex: function(el, eventName, fn) {
2373             for (var i = 0,len = listeners.length; i < len; ++i) {
2374                 var li = listeners[i];
2375                 if (li &&
2376                     li[this.FN] == fn &&
2377                     li[this.EL] == el &&
2378                     li[this.TYPE] == eventName) {
2379                     return i;
2380                 }
2381             }
2382
2383             return -1;
2384         },
2385
2386
2387         elCache: {},
2388
2389
2390         getEl: function(id) {
2391             return document.getElementById(id);
2392         },
2393
2394
2395         clearCache: function() {
2396         },
2397
2398
2399         _load: function(e) {
2400             loadComplete = true;
2401             var EU = Roo.lib.Event;
2402
2403
2404             if (Roo.isIE) {
2405                 EU.doRemove(window, "load", EU._load);
2406             }
2407         },
2408
2409
2410         _tryPreloadAttach: function() {
2411
2412             if (this.locked) {
2413                 return false;
2414             }
2415
2416             this.locked = true;
2417
2418
2419             var tryAgain = !loadComplete;
2420             if (!tryAgain) {
2421                 tryAgain = (retryCount > 0);
2422             }
2423
2424
2425             var notAvail = [];
2426             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2427                 var item = onAvailStack[i];
2428                 if (item) {
2429                     var el = this.getEl(item.id);
2430
2431                     if (el) {
2432                         if (!item.checkReady ||
2433                             loadComplete ||
2434                             el.nextSibling ||
2435                             (document && document.body)) {
2436
2437                             var scope = el;
2438                             if (item.override) {
2439                                 if (item.override === true) {
2440                                     scope = item.obj;
2441                                 } else {
2442                                     scope = item.override;
2443                                 }
2444                             }
2445                             item.fn.call(scope, item.obj);
2446                             onAvailStack[i] = null;
2447                         }
2448                     } else {
2449                         notAvail.push(item);
2450                     }
2451                 }
2452             }
2453
2454             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2455
2456             if (tryAgain) {
2457
2458                 this.startInterval();
2459             } else {
2460                 clearInterval(this._interval);
2461                 this._interval = null;
2462             }
2463
2464             this.locked = false;
2465
2466             return true;
2467
2468         },
2469
2470
2471         purgeElement: function(el, recurse, eventName) {
2472             var elListeners = this.getListeners(el, eventName);
2473             if (elListeners) {
2474                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2475                     var l = elListeners[i];
2476                     this.removeListener(el, l.type, l.fn);
2477                 }
2478             }
2479
2480             if (recurse && el && el.childNodes) {
2481                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2482                     this.purgeElement(el.childNodes[i], recurse, eventName);
2483                 }
2484             }
2485         },
2486
2487
2488         getListeners: function(el, eventName) {
2489             var results = [], searchLists;
2490             if (!eventName) {
2491                 searchLists = [listeners, unloadListeners];
2492             } else if (eventName == "unload") {
2493                 searchLists = [unloadListeners];
2494             } else {
2495                 searchLists = [listeners];
2496             }
2497
2498             for (var j = 0; j < searchLists.length; ++j) {
2499                 var searchList = searchLists[j];
2500                 if (searchList && searchList.length > 0) {
2501                     for (var i = 0,len = searchList.length; i < len; ++i) {
2502                         var l = searchList[i];
2503                         if (l && l[this.EL] === el &&
2504                             (!eventName || eventName === l[this.TYPE])) {
2505                             results.push({
2506                                 type:   l[this.TYPE],
2507                                 fn:     l[this.FN],
2508                                 obj:    l[this.OBJ],
2509                                 adjust: l[this.ADJ_SCOPE],
2510                                 index:  i
2511                             });
2512                         }
2513                     }
2514                 }
2515             }
2516
2517             return (results.length) ? results : null;
2518         },
2519
2520
2521         _unload: function(e) {
2522
2523             var EU = Roo.lib.Event, i, j, l, len, index;
2524
2525             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2526                 l = unloadListeners[i];
2527                 if (l) {
2528                     var scope = window;
2529                     if (l[EU.ADJ_SCOPE]) {
2530                         if (l[EU.ADJ_SCOPE] === true) {
2531                             scope = l[EU.OBJ];
2532                         } else {
2533                             scope = l[EU.ADJ_SCOPE];
2534                         }
2535                     }
2536                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2537                     unloadListeners[i] = null;
2538                     l = null;
2539                     scope = null;
2540                 }
2541             }
2542
2543             unloadListeners = null;
2544
2545             if (listeners && listeners.length > 0) {
2546                 j = listeners.length;
2547                 while (j) {
2548                     index = j - 1;
2549                     l = listeners[index];
2550                     if (l) {
2551                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2552                                 l[EU.FN], index);
2553                     }
2554                     j = j - 1;
2555                 }
2556                 l = null;
2557
2558                 EU.clearCache();
2559             }
2560
2561             EU.doRemove(window, "unload", EU._unload);
2562
2563         },
2564
2565
2566         getScroll: function() {
2567             var dd = document.documentElement, db = document.body;
2568             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2569                 return [dd.scrollTop, dd.scrollLeft];
2570             } else if (db) {
2571                 return [db.scrollTop, db.scrollLeft];
2572             } else {
2573                 return [0, 0];
2574             }
2575         },
2576
2577
2578         doAdd: function () {
2579             if (window.addEventListener) {
2580                 return function(el, eventName, fn, capture) {
2581                     el.addEventListener(eventName, fn, (capture));
2582                 };
2583             } else if (window.attachEvent) {
2584                 return function(el, eventName, fn, capture) {
2585                     el.attachEvent("on" + eventName, fn);
2586                 };
2587             } else {
2588                 return function() {
2589                 };
2590             }
2591         }(),
2592
2593
2594         doRemove: function() {
2595             if (window.removeEventListener) {
2596                 return function (el, eventName, fn, capture) {
2597                     el.removeEventListener(eventName, fn, (capture));
2598                 };
2599             } else if (window.detachEvent) {
2600                 return function (el, eventName, fn) {
2601                     el.detachEvent("on" + eventName, fn);
2602                 };
2603             } else {
2604                 return function() {
2605                 };
2606             }
2607         }()
2608     };
2609     
2610 }();
2611 (function() {     
2612    
2613     var E = Roo.lib.Event;
2614     E.on = E.addListener;
2615     E.un = E.removeListener;
2616
2617     if (document && document.body) {
2618         E._load();
2619     } else {
2620         E.doAdd(window, "load", E._load);
2621     }
2622     E.doAdd(window, "unload", E._unload);
2623     E._tryPreloadAttach();
2624 })();
2625
2626  
2627
2628 (function() {
2629     /**
2630      * @class Roo.lib.Ajax
2631      *
2632      * provide a simple Ajax request utility functions
2633      * 
2634      * Portions of this file are based on pieces of Yahoo User Interface Library
2635     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2636     * YUI licensed under the BSD License:
2637     * http://developer.yahoo.net/yui/license.txt
2638     * <script type="text/javascript">
2639     *
2640      *
2641      */
2642     Roo.lib.Ajax = {
2643         /**
2644          * @static 
2645          */
2646         request : function(method, uri, cb, data, options) {
2647             if(options){
2648                 var hs = options.headers;
2649                 if(hs){
2650                     for(var h in hs){
2651                         if(hs.hasOwnProperty(h)){
2652                             this.initHeader(h, hs[h], false);
2653                         }
2654                     }
2655                 }
2656                 if(options.xmlData){
2657                     this.initHeader('Content-Type', 'text/xml', false);
2658                     method = 'POST';
2659                     data = options.xmlData;
2660                 }
2661             }
2662
2663             return this.asyncRequest(method, uri, cb, data);
2664         },
2665         /**
2666          * serialize a form
2667          *
2668          * @static
2669          * @param {DomForm} form element
2670          * @return {String} urlencode form output.
2671          */
2672         serializeForm : function(form) {
2673             if(typeof form == 'string') {
2674                 form = (document.getElementById(form) || document.forms[form]);
2675             }
2676
2677             var el, name, val, disabled, data = '', hasSubmit = false;
2678             for (var i = 0; i < form.elements.length; i++) {
2679                 el = form.elements[i];
2680                 disabled = form.elements[i].disabled;
2681                 name = form.elements[i].name;
2682                 val = form.elements[i].value;
2683
2684                 if (!disabled && name){
2685                     switch (el.type)
2686                             {
2687                         case 'select-one':
2688                         case 'select-multiple':
2689                             for (var j = 0; j < el.options.length; j++) {
2690                                 if (el.options[j].selected) {
2691                                     if (Roo.isIE) {
2692                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2693                                     }
2694                                     else {
2695                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2696                                     }
2697                                 }
2698                             }
2699                             break;
2700                         case 'radio':
2701                         case 'checkbox':
2702                             if (el.checked) {
2703                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2704                             }
2705                             break;
2706                         case 'file':
2707
2708                         case undefined:
2709
2710                         case 'reset':
2711
2712                         case 'button':
2713
2714                             break;
2715                         case 'submit':
2716                             if(hasSubmit == false) {
2717                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2718                                 hasSubmit = true;
2719                             }
2720                             break;
2721                         default:
2722                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2723                             break;
2724                     }
2725                 }
2726             }
2727             data = data.substr(0, data.length - 1);
2728             return data;
2729         },
2730
2731         headers:{},
2732
2733         hasHeaders:false,
2734
2735         useDefaultHeader:true,
2736
2737         defaultPostHeader:'application/x-www-form-urlencoded',
2738
2739         useDefaultXhrHeader:true,
2740
2741         defaultXhrHeader:'XMLHttpRequest',
2742
2743         hasDefaultHeaders:true,
2744
2745         defaultHeaders:{},
2746
2747         poll:{},
2748
2749         timeout:{},
2750
2751         pollInterval:50,
2752
2753         transactionId:0,
2754
2755         setProgId:function(id)
2756         {
2757             this.activeX.unshift(id);
2758         },
2759
2760         setDefaultPostHeader:function(b)
2761         {
2762             this.useDefaultHeader = b;
2763         },
2764
2765         setDefaultXhrHeader:function(b)
2766         {
2767             this.useDefaultXhrHeader = b;
2768         },
2769
2770         setPollingInterval:function(i)
2771         {
2772             if (typeof i == 'number' && isFinite(i)) {
2773                 this.pollInterval = i;
2774             }
2775         },
2776
2777         createXhrObject:function(transactionId)
2778         {
2779             var obj,http;
2780             try
2781             {
2782
2783                 http = new XMLHttpRequest();
2784
2785                 obj = { conn:http, tId:transactionId };
2786             }
2787             catch(e)
2788             {
2789                 for (var i = 0; i < this.activeX.length; ++i) {
2790                     try
2791                     {
2792
2793                         http = new ActiveXObject(this.activeX[i]);
2794
2795                         obj = { conn:http, tId:transactionId };
2796                         break;
2797                     }
2798                     catch(e) {
2799                     }
2800                 }
2801             }
2802             finally
2803             {
2804                 return obj;
2805             }
2806         },
2807
2808         getConnectionObject:function()
2809         {
2810             var o;
2811             var tId = this.transactionId;
2812
2813             try
2814             {
2815                 o = this.createXhrObject(tId);
2816                 if (o) {
2817                     this.transactionId++;
2818                 }
2819             }
2820             catch(e) {
2821             }
2822             finally
2823             {
2824                 return o;
2825             }
2826         },
2827
2828         asyncRequest:function(method, uri, callback, postData)
2829         {
2830             var o = this.getConnectionObject();
2831
2832             if (!o) {
2833                 return null;
2834             }
2835             else {
2836                 o.conn.open(method, uri, true);
2837
2838                 if (this.useDefaultXhrHeader) {
2839                     if (!this.defaultHeaders['X-Requested-With']) {
2840                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2841                     }
2842                 }
2843
2844                 if(postData && this.useDefaultHeader){
2845                     this.initHeader('Content-Type', this.defaultPostHeader);
2846                 }
2847
2848                  if (this.hasDefaultHeaders || this.hasHeaders) {
2849                     this.setHeader(o);
2850                 }
2851
2852                 this.handleReadyState(o, callback);
2853                 o.conn.send(postData || null);
2854
2855                 return o;
2856             }
2857         },
2858
2859         handleReadyState:function(o, callback)
2860         {
2861             var oConn = this;
2862
2863             if (callback && callback.timeout) {
2864                 
2865                 this.timeout[o.tId] = window.setTimeout(function() {
2866                     oConn.abort(o, callback, true);
2867                 }, callback.timeout);
2868             }
2869
2870             this.poll[o.tId] = window.setInterval(
2871                     function() {
2872                         if (o.conn && o.conn.readyState == 4) {
2873                             window.clearInterval(oConn.poll[o.tId]);
2874                             delete oConn.poll[o.tId];
2875
2876                             if(callback && callback.timeout) {
2877                                 window.clearTimeout(oConn.timeout[o.tId]);
2878                                 delete oConn.timeout[o.tId];
2879                             }
2880
2881                             oConn.handleTransactionResponse(o, callback);
2882                         }
2883                     }
2884                     , this.pollInterval);
2885         },
2886
2887         handleTransactionResponse:function(o, callback, isAbort)
2888         {
2889
2890             if (!callback) {
2891                 this.releaseObject(o);
2892                 return;
2893             }
2894
2895             var httpStatus, responseObject;
2896
2897             try
2898             {
2899                 if (o.conn.status !== undefined && o.conn.status != 0) {
2900                     httpStatus = o.conn.status;
2901                 }
2902                 else {
2903                     httpStatus = 13030;
2904                 }
2905             }
2906             catch(e) {
2907
2908
2909                 httpStatus = 13030;
2910             }
2911
2912             if (httpStatus >= 200 && httpStatus < 300) {
2913                 responseObject = this.createResponseObject(o, callback.argument);
2914                 if (callback.success) {
2915                     if (!callback.scope) {
2916                         callback.success(responseObject);
2917                     }
2918                     else {
2919
2920
2921                         callback.success.apply(callback.scope, [responseObject]);
2922                     }
2923                 }
2924             }
2925             else {
2926                 switch (httpStatus) {
2927
2928                     case 12002:
2929                     case 12029:
2930                     case 12030:
2931                     case 12031:
2932                     case 12152:
2933                     case 13030:
2934                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2935                         if (callback.failure) {
2936                             if (!callback.scope) {
2937                                 callback.failure(responseObject);
2938                             }
2939                             else {
2940                                 callback.failure.apply(callback.scope, [responseObject]);
2941                             }
2942                         }
2943                         break;
2944                     default:
2945                         responseObject = this.createResponseObject(o, callback.argument);
2946                         if (callback.failure) {
2947                             if (!callback.scope) {
2948                                 callback.failure(responseObject);
2949                             }
2950                             else {
2951                                 callback.failure.apply(callback.scope, [responseObject]);
2952                             }
2953                         }
2954                 }
2955             }
2956
2957             this.releaseObject(o);
2958             responseObject = null;
2959         },
2960
2961         createResponseObject:function(o, callbackArg)
2962         {
2963             var obj = {};
2964             var headerObj = {};
2965
2966             try
2967             {
2968                 var headerStr = o.conn.getAllResponseHeaders();
2969                 var header = headerStr.split('\n');
2970                 for (var i = 0; i < header.length; i++) {
2971                     var delimitPos = header[i].indexOf(':');
2972                     if (delimitPos != -1) {
2973                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2974                     }
2975                 }
2976             }
2977             catch(e) {
2978             }
2979
2980             obj.tId = o.tId;
2981             obj.status = o.conn.status;
2982             obj.statusText = o.conn.statusText;
2983             obj.getResponseHeader = headerObj;
2984             obj.getAllResponseHeaders = headerStr;
2985             obj.responseText = o.conn.responseText;
2986             obj.responseXML = o.conn.responseXML;
2987
2988             if (typeof callbackArg !== undefined) {
2989                 obj.argument = callbackArg;
2990             }
2991
2992             return obj;
2993         },
2994
2995         createExceptionObject:function(tId, callbackArg, isAbort)
2996         {
2997             var COMM_CODE = 0;
2998             var COMM_ERROR = 'communication failure';
2999             var ABORT_CODE = -1;
3000             var ABORT_ERROR = 'transaction aborted';
3001
3002             var obj = {};
3003
3004             obj.tId = tId;
3005             if (isAbort) {
3006                 obj.status = ABORT_CODE;
3007                 obj.statusText = ABORT_ERROR;
3008             }
3009             else {
3010                 obj.status = COMM_CODE;
3011                 obj.statusText = COMM_ERROR;
3012             }
3013
3014             if (callbackArg) {
3015                 obj.argument = callbackArg;
3016             }
3017
3018             return obj;
3019         },
3020
3021         initHeader:function(label, value, isDefault)
3022         {
3023             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3024
3025             if (headerObj[label] === undefined) {
3026                 headerObj[label] = value;
3027             }
3028             else {
3029
3030
3031                 headerObj[label] = value + "," + headerObj[label];
3032             }
3033
3034             if (isDefault) {
3035                 this.hasDefaultHeaders = true;
3036             }
3037             else {
3038                 this.hasHeaders = true;
3039             }
3040         },
3041
3042
3043         setHeader:function(o)
3044         {
3045             if (this.hasDefaultHeaders) {
3046                 for (var prop in this.defaultHeaders) {
3047                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3048                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3049                     }
3050                 }
3051             }
3052
3053             if (this.hasHeaders) {
3054                 for (var prop in this.headers) {
3055                     if (this.headers.hasOwnProperty(prop)) {
3056                         o.conn.setRequestHeader(prop, this.headers[prop]);
3057                     }
3058                 }
3059                 this.headers = {};
3060                 this.hasHeaders = false;
3061             }
3062         },
3063
3064         resetDefaultHeaders:function() {
3065             delete this.defaultHeaders;
3066             this.defaultHeaders = {};
3067             this.hasDefaultHeaders = false;
3068         },
3069
3070         abort:function(o, callback, isTimeout)
3071         {
3072             if(this.isCallInProgress(o)) {
3073                 o.conn.abort();
3074                 window.clearInterval(this.poll[o.tId]);
3075                 delete this.poll[o.tId];
3076                 if (isTimeout) {
3077                     delete this.timeout[o.tId];
3078                 }
3079
3080                 this.handleTransactionResponse(o, callback, true);
3081
3082                 return true;
3083             }
3084             else {
3085                 return false;
3086             }
3087         },
3088
3089
3090         isCallInProgress:function(o)
3091         {
3092             if (o && o.conn) {
3093                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3094             }
3095             else {
3096
3097                 return false;
3098             }
3099         },
3100
3101
3102         releaseObject:function(o)
3103         {
3104
3105             o.conn = null;
3106
3107             o = null;
3108         },
3109
3110         activeX:[
3111         'MSXML2.XMLHTTP.3.0',
3112         'MSXML2.XMLHTTP',
3113         'Microsoft.XMLHTTP'
3114         ]
3115
3116
3117     };
3118 })();/*
3119  * Portions of this file are based on pieces of Yahoo User Interface Library
3120  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3121  * YUI licensed under the BSD License:
3122  * http://developer.yahoo.net/yui/license.txt
3123  * <script type="text/javascript">
3124  *
3125  */
3126
3127 Roo.lib.Region = function(t, r, b, l) {
3128     this.top = t;
3129     this[1] = t;
3130     this.right = r;
3131     this.bottom = b;
3132     this.left = l;
3133     this[0] = l;
3134 };
3135
3136
3137 Roo.lib.Region.prototype = {
3138     contains : function(region) {
3139         return ( region.left >= this.left &&
3140                  region.right <= this.right &&
3141                  region.top >= this.top &&
3142                  region.bottom <= this.bottom    );
3143
3144     },
3145
3146     getArea : function() {
3147         return ( (this.bottom - this.top) * (this.right - this.left) );
3148     },
3149
3150     intersect : function(region) {
3151         var t = Math.max(this.top, region.top);
3152         var r = Math.min(this.right, region.right);
3153         var b = Math.min(this.bottom, region.bottom);
3154         var l = Math.max(this.left, region.left);
3155
3156         if (b >= t && r >= l) {
3157             return new Roo.lib.Region(t, r, b, l);
3158         } else {
3159             return null;
3160         }
3161     },
3162     union : function(region) {
3163         var t = Math.min(this.top, region.top);
3164         var r = Math.max(this.right, region.right);
3165         var b = Math.max(this.bottom, region.bottom);
3166         var l = Math.min(this.left, region.left);
3167
3168         return new Roo.lib.Region(t, r, b, l);
3169     },
3170
3171     adjust : function(t, l, b, r) {
3172         this.top += t;
3173         this.left += l;
3174         this.right += r;
3175         this.bottom += b;
3176         return this;
3177     }
3178 };
3179
3180 Roo.lib.Region.getRegion = function(el) {
3181     var p = Roo.lib.Dom.getXY(el);
3182
3183     var t = p[1];
3184     var r = p[0] + el.offsetWidth;
3185     var b = p[1] + el.offsetHeight;
3186     var l = p[0];
3187
3188     return new Roo.lib.Region(t, r, b, l);
3189 };
3190 /*
3191  * Portions of this file are based on pieces of Yahoo User Interface Library
3192  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3193  * YUI licensed under the BSD License:
3194  * http://developer.yahoo.net/yui/license.txt
3195  * <script type="text/javascript">
3196  *
3197  */
3198 //@@dep Roo.lib.Region
3199
3200
3201 Roo.lib.Point = function(x, y) {
3202     if (x instanceof Array) {
3203         y = x[1];
3204         x = x[0];
3205     }
3206     this.x = this.right = this.left = this[0] = x;
3207     this.y = this.top = this.bottom = this[1] = y;
3208 };
3209
3210 Roo.lib.Point.prototype = new Roo.lib.Region();
3211 /*
3212  * Portions of this file are based on pieces of Yahoo User Interface Library
3213  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3214  * YUI licensed under the BSD License:
3215  * http://developer.yahoo.net/yui/license.txt
3216  * <script type="text/javascript">
3217  *
3218  */
3219  
3220 (function() {   
3221
3222     Roo.lib.Anim = {
3223         scroll : function(el, args, duration, easing, cb, scope) {
3224             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3225         },
3226
3227         motion : function(el, args, duration, easing, cb, scope) {
3228             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3229         },
3230
3231         color : function(el, args, duration, easing, cb, scope) {
3232             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3233         },
3234
3235         run : function(el, args, duration, easing, cb, scope, type) {
3236             type = type || Roo.lib.AnimBase;
3237             if (typeof easing == "string") {
3238                 easing = Roo.lib.Easing[easing];
3239             }
3240             var anim = new type(el, args, duration, easing);
3241             anim.animateX(function() {
3242                 Roo.callback(cb, scope);
3243             });
3244             return anim;
3245         }
3246     };
3247 })();/*
3248  * Portions of this file are based on pieces of Yahoo User Interface Library
3249  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3250  * YUI licensed under the BSD License:
3251  * http://developer.yahoo.net/yui/license.txt
3252  * <script type="text/javascript">
3253  *
3254  */
3255
3256 (function() {    
3257     var libFlyweight;
3258     
3259     function fly(el) {
3260         if (!libFlyweight) {
3261             libFlyweight = new Roo.Element.Flyweight();
3262         }
3263         libFlyweight.dom = el;
3264         return libFlyweight;
3265     }
3266
3267     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3268     
3269    
3270     
3271     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3272         if (el) {
3273             this.init(el, attributes, duration, method);
3274         }
3275     };
3276
3277     Roo.lib.AnimBase.fly = fly;
3278     
3279     
3280     
3281     Roo.lib.AnimBase.prototype = {
3282
3283         toString: function() {
3284             var el = this.getEl();
3285             var id = el.id || el.tagName;
3286             return ("Anim " + id);
3287         },
3288
3289         patterns: {
3290             noNegatives:        /width|height|opacity|padding/i,
3291             offsetAttribute:  /^((width|height)|(top|left))$/,
3292             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3293             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3294         },
3295
3296
3297         doMethod: function(attr, start, end) {
3298             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3299         },
3300
3301
3302         setAttribute: function(attr, val, unit) {
3303             if (this.patterns.noNegatives.test(attr)) {
3304                 val = (val > 0) ? val : 0;
3305             }
3306
3307             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3308         },
3309
3310
3311         getAttribute: function(attr) {
3312             var el = this.getEl();
3313             var val = fly(el).getStyle(attr);
3314
3315             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3316                 return parseFloat(val);
3317             }
3318
3319             var a = this.patterns.offsetAttribute.exec(attr) || [];
3320             var pos = !!( a[3] );
3321             var box = !!( a[2] );
3322
3323
3324             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3325                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3326             } else {
3327                 val = 0;
3328             }
3329
3330             return val;
3331         },
3332
3333
3334         getDefaultUnit: function(attr) {
3335             if (this.patterns.defaultUnit.test(attr)) {
3336                 return 'px';
3337             }
3338
3339             return '';
3340         },
3341
3342         animateX : function(callback, scope) {
3343             var f = function() {
3344                 this.onComplete.removeListener(f);
3345                 if (typeof callback == "function") {
3346                     callback.call(scope || this, this);
3347                 }
3348             };
3349             this.onComplete.addListener(f, this);
3350             this.animate();
3351         },
3352
3353
3354         setRuntimeAttribute: function(attr) {
3355             var start;
3356             var end;
3357             var attributes = this.attributes;
3358
3359             this.runtimeAttributes[attr] = {};
3360
3361             var isset = function(prop) {
3362                 return (typeof prop !== 'undefined');
3363             };
3364
3365             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3366                 return false;
3367             }
3368
3369             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3370
3371
3372             if (isset(attributes[attr]['to'])) {
3373                 end = attributes[attr]['to'];
3374             } else if (isset(attributes[attr]['by'])) {
3375                 if (start.constructor == Array) {
3376                     end = [];
3377                     for (var i = 0, len = start.length; i < len; ++i) {
3378                         end[i] = start[i] + attributes[attr]['by'][i];
3379                     }
3380                 } else {
3381                     end = start + attributes[attr]['by'];
3382                 }
3383             }
3384
3385             this.runtimeAttributes[attr].start = start;
3386             this.runtimeAttributes[attr].end = end;
3387
3388
3389             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3390         },
3391
3392
3393         init: function(el, attributes, duration, method) {
3394
3395             var isAnimated = false;
3396
3397
3398             var startTime = null;
3399
3400
3401             var actualFrames = 0;
3402
3403
3404             el = Roo.getDom(el);
3405
3406
3407             this.attributes = attributes || {};
3408
3409
3410             this.duration = duration || 1;
3411
3412
3413             this.method = method || Roo.lib.Easing.easeNone;
3414
3415
3416             this.useSeconds = true;
3417
3418
3419             this.currentFrame = 0;
3420
3421
3422             this.totalFrames = Roo.lib.AnimMgr.fps;
3423
3424
3425             this.getEl = function() {
3426                 return el;
3427             };
3428
3429
3430             this.isAnimated = function() {
3431                 return isAnimated;
3432             };
3433
3434
3435             this.getStartTime = function() {
3436                 return startTime;
3437             };
3438
3439             this.runtimeAttributes = {};
3440
3441
3442             this.animate = function() {
3443                 if (this.isAnimated()) {
3444                     return false;
3445                 }
3446
3447                 this.currentFrame = 0;
3448
3449                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3450
3451                 Roo.lib.AnimMgr.registerElement(this);
3452             };
3453
3454
3455             this.stop = function(finish) {
3456                 if (finish) {
3457                     this.currentFrame = this.totalFrames;
3458                     this._onTween.fire();
3459                 }
3460                 Roo.lib.AnimMgr.stop(this);
3461             };
3462
3463             var onStart = function() {
3464                 this.onStart.fire();
3465
3466                 this.runtimeAttributes = {};
3467                 for (var attr in this.attributes) {
3468                     this.setRuntimeAttribute(attr);
3469                 }
3470
3471                 isAnimated = true;
3472                 actualFrames = 0;
3473                 startTime = new Date();
3474             };
3475
3476
3477             var onTween = function() {
3478                 var data = {
3479                     duration: new Date() - this.getStartTime(),
3480                     currentFrame: this.currentFrame
3481                 };
3482
3483                 data.toString = function() {
3484                     return (
3485                             'duration: ' + data.duration +
3486                             ', currentFrame: ' + data.currentFrame
3487                             );
3488                 };
3489
3490                 this.onTween.fire(data);
3491
3492                 var runtimeAttributes = this.runtimeAttributes;
3493
3494                 for (var attr in runtimeAttributes) {
3495                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3496                 }
3497
3498                 actualFrames += 1;
3499             };
3500
3501             var onComplete = function() {
3502                 var actual_duration = (new Date() - startTime) / 1000 ;
3503
3504                 var data = {
3505                     duration: actual_duration,
3506                     frames: actualFrames,
3507                     fps: actualFrames / actual_duration
3508                 };
3509
3510                 data.toString = function() {
3511                     return (
3512                             'duration: ' + data.duration +
3513                             ', frames: ' + data.frames +
3514                             ', fps: ' + data.fps
3515                             );
3516                 };
3517
3518                 isAnimated = false;
3519                 actualFrames = 0;
3520                 this.onComplete.fire(data);
3521             };
3522
3523
3524             this._onStart = new Roo.util.Event(this);
3525             this.onStart = new Roo.util.Event(this);
3526             this.onTween = new Roo.util.Event(this);
3527             this._onTween = new Roo.util.Event(this);
3528             this.onComplete = new Roo.util.Event(this);
3529             this._onComplete = new Roo.util.Event(this);
3530             this._onStart.addListener(onStart);
3531             this._onTween.addListener(onTween);
3532             this._onComplete.addListener(onComplete);
3533         }
3534     };
3535 })();
3536 /*
3537  * Portions of this file are based on pieces of Yahoo User Interface Library
3538  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3539  * YUI licensed under the BSD License:
3540  * http://developer.yahoo.net/yui/license.txt
3541  * <script type="text/javascript">
3542  *
3543  */
3544
3545 Roo.lib.AnimMgr = new function() {
3546
3547     var thread = null;
3548
3549
3550     var queue = [];
3551
3552
3553     var tweenCount = 0;
3554
3555
3556     this.fps = 1000;
3557
3558
3559     this.delay = 1;
3560
3561
3562     this.registerElement = function(tween) {
3563         queue[queue.length] = tween;
3564         tweenCount += 1;
3565         tween._onStart.fire();
3566         this.start();
3567     };
3568
3569
3570     this.unRegister = function(tween, index) {
3571         tween._onComplete.fire();
3572         index = index || getIndex(tween);
3573         if (index != -1) {
3574             queue.splice(index, 1);
3575         }
3576
3577         tweenCount -= 1;
3578         if (tweenCount <= 0) {
3579             this.stop();
3580         }
3581     };
3582
3583
3584     this.start = function() {
3585         if (thread === null) {
3586             thread = setInterval(this.run, this.delay);
3587         }
3588     };
3589
3590
3591     this.stop = function(tween) {
3592         if (!tween) {
3593             clearInterval(thread);
3594
3595             for (var i = 0, len = queue.length; i < len; ++i) {
3596                 if (queue[0].isAnimated()) {
3597                     this.unRegister(queue[0], 0);
3598                 }
3599             }
3600
3601             queue = [];
3602             thread = null;
3603             tweenCount = 0;
3604         }
3605         else {
3606             this.unRegister(tween);
3607         }
3608     };
3609
3610
3611     this.run = function() {
3612         for (var i = 0, len = queue.length; i < len; ++i) {
3613             var tween = queue[i];
3614             if (!tween || !tween.isAnimated()) {
3615                 continue;
3616             }
3617
3618             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3619             {
3620                 tween.currentFrame += 1;
3621
3622                 if (tween.useSeconds) {
3623                     correctFrame(tween);
3624                 }
3625                 tween._onTween.fire();
3626             }
3627             else {
3628                 Roo.lib.AnimMgr.stop(tween, i);
3629             }
3630         }
3631     };
3632
3633     var getIndex = function(anim) {
3634         for (var i = 0, len = queue.length; i < len; ++i) {
3635             if (queue[i] == anim) {
3636                 return i;
3637             }
3638         }
3639         return -1;
3640     };
3641
3642
3643     var correctFrame = function(tween) {
3644         var frames = tween.totalFrames;
3645         var frame = tween.currentFrame;
3646         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3647         var elapsed = (new Date() - tween.getStartTime());
3648         var tweak = 0;
3649
3650         if (elapsed < tween.duration * 1000) {
3651             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3652         } else {
3653             tweak = frames - (frame + 1);
3654         }
3655         if (tweak > 0 && isFinite(tweak)) {
3656             if (tween.currentFrame + tweak >= frames) {
3657                 tweak = frames - (frame + 1);
3658             }
3659
3660             tween.currentFrame += tweak;
3661         }
3662     };
3663 };
3664
3665     /*
3666  * Portions of this file are based on pieces of Yahoo User Interface Library
3667  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3668  * YUI licensed under the BSD License:
3669  * http://developer.yahoo.net/yui/license.txt
3670  * <script type="text/javascript">
3671  *
3672  */
3673 Roo.lib.Bezier = new function() {
3674
3675         this.getPosition = function(points, t) {
3676             var n = points.length;
3677             var tmp = [];
3678
3679             for (var i = 0; i < n; ++i) {
3680                 tmp[i] = [points[i][0], points[i][1]];
3681             }
3682
3683             for (var j = 1; j < n; ++j) {
3684                 for (i = 0; i < n - j; ++i) {
3685                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3686                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3687                 }
3688             }
3689
3690             return [ tmp[0][0], tmp[0][1] ];
3691
3692         };
3693     }; 
3694
3695 /**
3696  * @class Roo.lib.Color
3697  * @constructor
3698  * An abstract Color implementation. Concrete Color implementations should use
3699  * an instance of this function as their prototype, and implement the getRGB and
3700  * getHSL functions. getRGB should return an object representing the RGB
3701  * components of this Color, with the red, green, and blue components in the
3702  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3703  * return an object representing the HSL components of this Color, with the hue
3704  * component in the range [0,360), the saturation and lightness components in
3705  * the range [0,100], and the alpha component in the range [0,1].
3706  *
3707  *
3708  * Color.js
3709  *
3710  * Functions for Color handling and processing.
3711  *
3712  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3713  *
3714  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3715  * rights to this program, with the intention of it becoming part of the public
3716  * domain. Because this program is released into the public domain, it comes with
3717  * no warranty either expressed or implied, to the extent permitted by law.
3718  * 
3719  * For more free and public domain JavaScript code by the same author, visit:
3720  * http://www.safalra.com/web-design/javascript/
3721  * 
3722  */
3723 Roo.lib.Color = function() { }
3724
3725
3726 Roo.apply(Roo.lib.Color.prototype, {
3727   
3728   rgb : null,
3729   hsv : null,
3730   hsl : null,
3731   
3732   /**
3733    * getIntegerRGB
3734    * @return {Object} an object representing the RGBA components of this Color. The red,
3735    * green, and blue components are converted to integers in the range [0,255].
3736    * The alpha is a value in the range [0,1].
3737    */
3738   getIntegerRGB : function(){
3739
3740     // get the RGB components of this Color
3741     var rgb = this.getRGB();
3742
3743     // return the integer components
3744     return {
3745       'r' : Math.round(rgb.r),
3746       'g' : Math.round(rgb.g),
3747       'b' : Math.round(rgb.b),
3748       'a' : rgb.a
3749     };
3750
3751   },
3752
3753   /**
3754    * getPercentageRGB
3755    * @return {Object} an object representing the RGBA components of this Color. The red,
3756    * green, and blue components are converted to numbers in the range [0,100].
3757    * The alpha is a value in the range [0,1].
3758    */
3759   getPercentageRGB : function(){
3760
3761     // get the RGB components of this Color
3762     var rgb = this.getRGB();
3763
3764     // return the percentage components
3765     return {
3766       'r' : 100 * rgb.r / 255,
3767       'g' : 100 * rgb.g / 255,
3768       'b' : 100 * rgb.b / 255,
3769       'a' : rgb.a
3770     };
3771
3772   },
3773
3774   /**
3775    * getCSSHexadecimalRGB
3776    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3777    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3778    * are two-digit hexadecimal numbers.
3779    */
3780   getCSSHexadecimalRGB : function()
3781   {
3782
3783     // get the integer RGB components
3784     var rgb = this.getIntegerRGB();
3785
3786     // determine the hexadecimal equivalents
3787     var r16 = rgb.r.toString(16);
3788     var g16 = rgb.g.toString(16);
3789     var b16 = rgb.b.toString(16);
3790
3791     // return the CSS RGB Color value
3792     return '#'
3793         + (r16.length == 2 ? r16 : '0' + r16)
3794         + (g16.length == 2 ? g16 : '0' + g16)
3795         + (b16.length == 2 ? b16 : '0' + b16);
3796
3797   },
3798
3799   /**
3800    * getCSSIntegerRGB
3801    * @return {String} a string representing this Color as a CSS integer RGB Color
3802    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3803    * are integers in the range [0,255].
3804    */
3805   getCSSIntegerRGB : function(){
3806
3807     // get the integer RGB components
3808     var rgb = this.getIntegerRGB();
3809
3810     // return the CSS RGB Color value
3811     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3812
3813   },
3814
3815   /**
3816    * getCSSIntegerRGBA
3817    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3818    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3819    * b are integers in the range [0,255] and a is in the range [0,1].
3820    */
3821   getCSSIntegerRGBA : function(){
3822
3823     // get the integer RGB components
3824     var rgb = this.getIntegerRGB();
3825
3826     // return the CSS integer RGBA Color value
3827     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3828
3829   },
3830
3831   /**
3832    * getCSSPercentageRGB
3833    * @return {String} a string representing this Color as a CSS percentage RGB Color
3834    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3835    * b are in the range [0,100].
3836    */
3837   getCSSPercentageRGB : function(){
3838
3839     // get the percentage RGB components
3840     var rgb = this.getPercentageRGB();
3841
3842     // return the CSS RGB Color value
3843     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3844
3845   },
3846
3847   /**
3848    * getCSSPercentageRGBA
3849    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3850    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3851    * and b are in the range [0,100] and a is in the range [0,1].
3852    */
3853   getCSSPercentageRGBA : function(){
3854
3855     // get the percentage RGB components
3856     var rgb = this.getPercentageRGB();
3857
3858     // return the CSS percentage RGBA Color value
3859     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3860
3861   },
3862
3863   /**
3864    * getCSSHSL
3865    * @return {String} a string representing this Color as a CSS HSL Color value - that
3866    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3867    * s and l are in the range [0,100].
3868    */
3869   getCSSHSL : function(){
3870
3871     // get the HSL components
3872     var hsl = this.getHSL();
3873
3874     // return the CSS HSL Color value
3875     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3876
3877   },
3878
3879   /**
3880    * getCSSHSLA
3881    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3882    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3883    * s and l are in the range [0,100], and a is in the range [0,1].
3884    */
3885   getCSSHSLA : function(){
3886
3887     // get the HSL components
3888     var hsl = this.getHSL();
3889
3890     // return the CSS HSL Color value
3891     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3892
3893   },
3894
3895   /**
3896    * Sets the Color of the specified node to this Color. This functions sets
3897    * the CSS 'color' property for the node. The parameter is:
3898    * 
3899    * @param {DomElement} node - the node whose Color should be set
3900    */
3901   setNodeColor : function(node){
3902
3903     // set the Color of the node
3904     node.style.color = this.getCSSHexadecimalRGB();
3905
3906   },
3907
3908   /**
3909    * Sets the background Color of the specified node to this Color. This
3910    * functions sets the CSS 'background-color' property for the node. The
3911    * parameter is:
3912    *
3913    * @param {DomElement} node - the node whose background Color should be set
3914    */
3915   setNodeBackgroundColor : function(node){
3916
3917     // set the background Color of the node
3918     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3919
3920   },
3921   // convert between formats..
3922   toRGB: function()
3923   {
3924     var r = this.getIntegerRGB();
3925     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3926     
3927   },
3928   toHSL : function()
3929   {
3930      var hsl = this.getHSL();
3931   // return the CSS HSL Color value
3932     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3933     
3934   },
3935   
3936   toHSV : function()
3937   {
3938     var rgb = this.toRGB();
3939     var hsv = rgb.getHSV();
3940    // return the CSS HSL Color value
3941     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3942     
3943   },
3944   
3945   // modify  v = 0 ... 1 (eg. 0.5)
3946   saturate : function(v)
3947   {
3948       var rgb = this.toRGB();
3949       var hsv = rgb.getHSV();
3950       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3951       
3952   
3953   },
3954   
3955    
3956   /**
3957    * getRGB
3958    * @return {Object} the RGB and alpha components of this Color as an object with r,
3959    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3960    * the range [0,1].
3961    */
3962   getRGB: function(){
3963    
3964     // return the RGB components
3965     return {
3966       'r' : this.rgb.r,
3967       'g' : this.rgb.g,
3968       'b' : this.rgb.b,
3969       'a' : this.alpha
3970     };
3971
3972   },
3973
3974   /**
3975    * getHSV
3976    * @return {Object} the HSV and alpha components of this Color as an object with h,
3977    * s, v, and a properties. h is in the range [0,360), s and v are in the range
3978    * [0,100], and a is in the range [0,1].
3979    */
3980   getHSV : function()
3981   {
3982     
3983     // calculate the HSV components if necessary
3984     if (this.hsv == null) {
3985       this.calculateHSV();
3986     }
3987
3988     // return the HSV components
3989     return {
3990       'h' : this.hsv.h,
3991       's' : this.hsv.s,
3992       'v' : this.hsv.v,
3993       'a' : this.alpha
3994     };
3995
3996   },
3997
3998   /**
3999    * getHSL
4000    * @return {Object} the HSL and alpha components of this Color as an object with h,
4001    * s, l, and a properties. h is in the range [0,360), s and l are in the range
4002    * [0,100], and a is in the range [0,1].
4003    */
4004   getHSL : function(){
4005     
4006      
4007     // calculate the HSV components if necessary
4008     if (this.hsl == null) { this.calculateHSL(); }
4009
4010     // return the HSL components
4011     return {
4012       'h' : this.hsl.h,
4013       's' : this.hsl.s,
4014       'l' : this.hsl.l,
4015       'a' : this.alpha
4016     };
4017
4018   }
4019   
4020
4021 });
4022
4023
4024 /**
4025  * @class Roo.lib.RGBColor
4026  * @extends Roo.lib.Color
4027  * Creates a Color specified in the RGB Color space, with an optional alpha
4028  * component. The parameters are:
4029  * @constructor
4030  * 
4031
4032  * @param {Number} r - the red component, clipped to the range [0,255]
4033  * @param {Number} g - the green component, clipped to the range [0,255]
4034  * @param {Number} b - the blue component, clipped to the range [0,255]
4035  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4036  *     optional and defaults to 1
4037  */
4038 Roo.lib.RGBColor = function (r, g, b, a){
4039
4040   // store the alpha component after clipping it if necessary
4041   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4042
4043   // store the RGB components after clipping them if necessary
4044   this.rgb =
4045       {
4046         'r' : Math.max(0, Math.min(255, r)),
4047         'g' : Math.max(0, Math.min(255, g)),
4048         'b' : Math.max(0, Math.min(255, b))
4049       };
4050
4051   // initialise the HSV and HSL components to null
4052   
4053
4054   /* 
4055    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4056    * range [0,360). The parameters are:
4057    *
4058    * maximum - the maximum of the RGB component values
4059    * range   - the range of the RGB component values
4060    */
4061    
4062
4063 }
4064 // this does an 'exteds'
4065 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4066
4067   
4068     getHue  : function(maximum, range)
4069     {
4070       var rgb = this.rgb;
4071        
4072       // check whether the range is zero
4073       if (range == 0){
4074   
4075         // set the hue to zero (any hue is acceptable as the Color is grey)
4076         var hue = 0;
4077   
4078       }else{
4079   
4080         // determine which of the components has the highest value and set the hue
4081         switch (maximum){
4082   
4083           // red has the highest value
4084           case rgb.r:
4085             var hue = (rgb.g - rgb.b) / range * 60;
4086             if (hue < 0) { hue += 360; }
4087             break;
4088   
4089           // green has the highest value
4090           case rgb.g:
4091             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4092             break;
4093   
4094           // blue has the highest value
4095           case rgb.b:
4096             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4097             break;
4098   
4099         }
4100   
4101       }
4102   
4103       // return the hue
4104       return hue;
4105   
4106     },
4107
4108   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4109    * be returned be the getHSV function.
4110    */
4111    calculateHSV : function(){
4112     var rgb = this.rgb;
4113     // get the maximum and range of the RGB component values
4114     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4115     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4116
4117     // store the HSV components
4118     this.hsv =
4119         {
4120           'h' : this.getHue(maximum, range),
4121           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4122           'v' : maximum / 2.55
4123         };
4124
4125   },
4126
4127   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4128    * be returned be the getHSL function.
4129    */
4130    calculateHSL : function(){
4131     var rgb = this.rgb;
4132     // get the maximum and range of the RGB component values
4133     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4134     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4135
4136     // determine the lightness in the range [0,1]
4137     var l = maximum / 255 - range / 510;
4138
4139     // store the HSL components
4140     this.hsl =
4141         {
4142           'h' : this.getHue(maximum, range),
4143           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4144           'l' : 100 * l
4145         };
4146
4147   }
4148
4149 });
4150
4151 /**
4152  * @class Roo.lib.HSVColor
4153  * @extends Roo.lib.Color
4154  * Creates a Color specified in the HSV Color space, with an optional alpha
4155  * component. The parameters are:
4156  * @constructor
4157  *
4158  * @param {Number} h - the hue component, wrapped to the range [0,360)
4159  * @param {Number} s - the saturation component, clipped to the range [0,100]
4160  * @param {Number} v - the value component, clipped to the range [0,100]
4161  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4162  *     optional and defaults to 1
4163  */
4164 Roo.lib.HSVColor = function (h, s, v, a){
4165
4166   // store the alpha component after clipping it if necessary
4167   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4168
4169   // store the HSV components after clipping or wrapping them if necessary
4170   this.hsv =
4171       {
4172         'h' : (h % 360 + 360) % 360,
4173         's' : Math.max(0, Math.min(100, s)),
4174         'v' : Math.max(0, Math.min(100, v))
4175       };
4176
4177   // initialise the RGB and HSL components to null
4178   this.rgb = null;
4179   this.hsl = null;
4180 }
4181
4182 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4183   /* Calculates and stores the RGB components of this HSVColor so that they can
4184    * be returned be the getRGB function.
4185    */
4186   calculateRGB: function ()
4187   {
4188     var hsv = this.hsv;
4189     // check whether the saturation is zero
4190     if (hsv.s == 0){
4191
4192       // set the Color to the appropriate shade of grey
4193       var r = hsv.v;
4194       var g = hsv.v;
4195       var b = hsv.v;
4196
4197     }else{
4198
4199       // set some temporary values
4200       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4201       var p  = hsv.v * (1 - hsv.s / 100);
4202       var q  = hsv.v * (1 - hsv.s / 100 * f);
4203       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4204
4205       // set the RGB Color components to their temporary values
4206       switch (Math.floor(hsv.h / 60)){
4207         case 0: var r = hsv.v; var g = t; var b = p; break;
4208         case 1: var r = q; var g = hsv.v; var b = p; break;
4209         case 2: var r = p; var g = hsv.v; var b = t; break;
4210         case 3: var r = p; var g = q; var b = hsv.v; break;
4211         case 4: var r = t; var g = p; var b = hsv.v; break;
4212         case 5: var r = hsv.v; var g = p; var b = q; break;
4213       }
4214
4215     }
4216
4217     // store the RGB components
4218     this.rgb =
4219         {
4220           'r' : r * 2.55,
4221           'g' : g * 2.55,
4222           'b' : b * 2.55
4223         };
4224
4225   },
4226
4227   /* Calculates and stores the HSL components of this HSVColor so that they can
4228    * be returned be the getHSL function.
4229    */
4230   calculateHSL : function (){
4231
4232     var hsv = this.hsv;
4233     // determine the lightness in the range [0,100]
4234     var l = (2 - hsv.s / 100) * hsv.v / 2;
4235
4236     // store the HSL components
4237     this.hsl =
4238         {
4239           'h' : hsv.h,
4240           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4241           'l' : l
4242         };
4243
4244     // correct a division-by-zero error
4245     if (isNaN(hsl.s)) { hsl.s = 0; }
4246
4247   } 
4248  
4249
4250 });
4251  
4252
4253 /**
4254  * @class Roo.lib.HSLColor
4255  * @extends Roo.lib.Color
4256  *
4257  * @constructor
4258  * Creates a Color specified in the HSL Color space, with an optional alpha
4259  * component. The parameters are:
4260  *
4261  * @param {Number} h - the hue component, wrapped to the range [0,360)
4262  * @param {Number} s - the saturation component, clipped to the range [0,100]
4263  * @param {Number} l - the lightness component, clipped to the range [0,100]
4264  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4265  *     optional and defaults to 1
4266  */
4267
4268 Roo.lib.HSLColor = function(h, s, l, a){
4269
4270   // store the alpha component after clipping it if necessary
4271   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4272
4273   // store the HSL components after clipping or wrapping them if necessary
4274   this.hsl =
4275       {
4276         'h' : (h % 360 + 360) % 360,
4277         's' : Math.max(0, Math.min(100, s)),
4278         'l' : Math.max(0, Math.min(100, l))
4279       };
4280
4281   // initialise the RGB and HSV components to null
4282 }
4283
4284 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4285
4286   /* Calculates and stores the RGB components of this HSLColor so that they can
4287    * be returned be the getRGB function.
4288    */
4289   calculateRGB: function (){
4290
4291     // check whether the saturation is zero
4292     if (this.hsl.s == 0){
4293
4294       // store the RGB components representing the appropriate shade of grey
4295       this.rgb =
4296           {
4297             'r' : this.hsl.l * 2.55,
4298             'g' : this.hsl.l * 2.55,
4299             'b' : this.hsl.l * 2.55
4300           };
4301
4302     }else{
4303
4304       // set some temporary values
4305       var p = this.hsl.l < 50
4306             ? this.hsl.l * (1 + hsl.s / 100)
4307             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4308       var q = 2 * hsl.l - p;
4309
4310       // initialise the RGB components
4311       this.rgb =
4312           {
4313             'r' : (h + 120) / 60 % 6,
4314             'g' : h / 60,
4315             'b' : (h + 240) / 60 % 6
4316           };
4317
4318       // loop over the RGB components
4319       for (var key in this.rgb){
4320
4321         // ensure that the property is not inherited from the root object
4322         if (this.rgb.hasOwnProperty(key)){
4323
4324           // set the component to its value in the range [0,100]
4325           if (this.rgb[key] < 1){
4326             this.rgb[key] = q + (p - q) * this.rgb[key];
4327           }else if (this.rgb[key] < 3){
4328             this.rgb[key] = p;
4329           }else if (this.rgb[key] < 4){
4330             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4331           }else{
4332             this.rgb[key] = q;
4333           }
4334
4335           // set the component to its value in the range [0,255]
4336           this.rgb[key] *= 2.55;
4337
4338         }
4339
4340       }
4341
4342     }
4343
4344   },
4345
4346   /* Calculates and stores the HSV components of this HSLColor so that they can
4347    * be returned be the getHSL function.
4348    */
4349    calculateHSV : function(){
4350
4351     // set a temporary value
4352     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4353
4354     // store the HSV components
4355     this.hsv =
4356         {
4357           'h' : this.hsl.h,
4358           's' : 200 * t / (this.hsl.l + t),
4359           'v' : t + this.hsl.l
4360         };
4361
4362     // correct a division-by-zero error
4363     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4364
4365   }
4366  
4367
4368 });
4369 /*
4370  * Portions of this file are based on pieces of Yahoo User Interface Library
4371  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4372  * YUI licensed under the BSD License:
4373  * http://developer.yahoo.net/yui/license.txt
4374  * <script type="text/javascript">
4375  *
4376  */
4377 (function() {
4378
4379     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4380         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4381     };
4382
4383     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4384
4385     var fly = Roo.lib.AnimBase.fly;
4386     var Y = Roo.lib;
4387     var superclass = Y.ColorAnim.superclass;
4388     var proto = Y.ColorAnim.prototype;
4389
4390     proto.toString = function() {
4391         var el = this.getEl();
4392         var id = el.id || el.tagName;
4393         return ("ColorAnim " + id);
4394     };
4395
4396     proto.patterns.color = /color$/i;
4397     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4398     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4399     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4400     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4401
4402
4403     proto.parseColor = function(s) {
4404         if (s.length == 3) {
4405             return s;
4406         }
4407
4408         var c = this.patterns.hex.exec(s);
4409         if (c && c.length == 4) {
4410             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4411         }
4412
4413         c = this.patterns.rgb.exec(s);
4414         if (c && c.length == 4) {
4415             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4416         }
4417
4418         c = this.patterns.hex3.exec(s);
4419         if (c && c.length == 4) {
4420             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4421         }
4422
4423         return null;
4424     };
4425     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4426     proto.getAttribute = function(attr) {
4427         var el = this.getEl();
4428         if (this.patterns.color.test(attr)) {
4429             var val = fly(el).getStyle(attr);
4430
4431             if (this.patterns.transparent.test(val)) {
4432                 var parent = el.parentNode;
4433                 val = fly(parent).getStyle(attr);
4434
4435                 while (parent && this.patterns.transparent.test(val)) {
4436                     parent = parent.parentNode;
4437                     val = fly(parent).getStyle(attr);
4438                     if (parent.tagName.toUpperCase() == 'HTML') {
4439                         val = '#fff';
4440                     }
4441                 }
4442             }
4443         } else {
4444             val = superclass.getAttribute.call(this, attr);
4445         }
4446
4447         return val;
4448     };
4449     proto.getAttribute = function(attr) {
4450         var el = this.getEl();
4451         if (this.patterns.color.test(attr)) {
4452             var val = fly(el).getStyle(attr);
4453
4454             if (this.patterns.transparent.test(val)) {
4455                 var parent = el.parentNode;
4456                 val = fly(parent).getStyle(attr);
4457
4458                 while (parent && this.patterns.transparent.test(val)) {
4459                     parent = parent.parentNode;
4460                     val = fly(parent).getStyle(attr);
4461                     if (parent.tagName.toUpperCase() == 'HTML') {
4462                         val = '#fff';
4463                     }
4464                 }
4465             }
4466         } else {
4467             val = superclass.getAttribute.call(this, attr);
4468         }
4469
4470         return val;
4471     };
4472
4473     proto.doMethod = function(attr, start, end) {
4474         var val;
4475
4476         if (this.patterns.color.test(attr)) {
4477             val = [];
4478             for (var i = 0, len = start.length; i < len; ++i) {
4479                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4480             }
4481
4482             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4483         }
4484         else {
4485             val = superclass.doMethod.call(this, attr, start, end);
4486         }
4487
4488         return val;
4489     };
4490
4491     proto.setRuntimeAttribute = function(attr) {
4492         superclass.setRuntimeAttribute.call(this, attr);
4493
4494         if (this.patterns.color.test(attr)) {
4495             var attributes = this.attributes;
4496             var start = this.parseColor(this.runtimeAttributes[attr].start);
4497             var end = this.parseColor(this.runtimeAttributes[attr].end);
4498
4499             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4500                 end = this.parseColor(attributes[attr].by);
4501
4502                 for (var i = 0, len = start.length; i < len; ++i) {
4503                     end[i] = start[i] + end[i];
4504                 }
4505             }
4506
4507             this.runtimeAttributes[attr].start = start;
4508             this.runtimeAttributes[attr].end = end;
4509         }
4510     };
4511 })();
4512
4513 /*
4514  * Portions of this file are based on pieces of Yahoo User Interface Library
4515  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4516  * YUI licensed under the BSD License:
4517  * http://developer.yahoo.net/yui/license.txt
4518  * <script type="text/javascript">
4519  *
4520  */
4521 Roo.lib.Easing = {
4522
4523
4524     easeNone: function (t, b, c, d) {
4525         return c * t / d + b;
4526     },
4527
4528
4529     easeIn: function (t, b, c, d) {
4530         return c * (t /= d) * t + b;
4531     },
4532
4533
4534     easeOut: function (t, b, c, d) {
4535         return -c * (t /= d) * (t - 2) + b;
4536     },
4537
4538
4539     easeBoth: function (t, b, c, d) {
4540         if ((t /= d / 2) < 1) {
4541             return c / 2 * t * t + b;
4542         }
4543
4544         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4545     },
4546
4547
4548     easeInStrong: function (t, b, c, d) {
4549         return c * (t /= d) * t * t * t + b;
4550     },
4551
4552
4553     easeOutStrong: function (t, b, c, d) {
4554         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4555     },
4556
4557
4558     easeBothStrong: function (t, b, c, d) {
4559         if ((t /= d / 2) < 1) {
4560             return c / 2 * t * t * t * t + b;
4561         }
4562
4563         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4564     },
4565
4566
4567
4568     elasticIn: function (t, b, c, d, a, p) {
4569         if (t == 0) {
4570             return b;
4571         }
4572         if ((t /= d) == 1) {
4573             return b + c;
4574         }
4575         if (!p) {
4576             p = d * .3;
4577         }
4578
4579         if (!a || a < Math.abs(c)) {
4580             a = c;
4581             var s = p / 4;
4582         }
4583         else {
4584             var s = p / (2 * Math.PI) * Math.asin(c / a);
4585         }
4586
4587         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4588     },
4589
4590
4591     elasticOut: function (t, b, c, d, a, p) {
4592         if (t == 0) {
4593             return b;
4594         }
4595         if ((t /= d) == 1) {
4596             return b + c;
4597         }
4598         if (!p) {
4599             p = d * .3;
4600         }
4601
4602         if (!a || a < Math.abs(c)) {
4603             a = c;
4604             var s = p / 4;
4605         }
4606         else {
4607             var s = p / (2 * Math.PI) * Math.asin(c / a);
4608         }
4609
4610         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4611     },
4612
4613
4614     elasticBoth: function (t, b, c, d, a, p) {
4615         if (t == 0) {
4616             return b;
4617         }
4618
4619         if ((t /= d / 2) == 2) {
4620             return b + c;
4621         }
4622
4623         if (!p) {
4624             p = d * (.3 * 1.5);
4625         }
4626
4627         if (!a || a < Math.abs(c)) {
4628             a = c;
4629             var s = p / 4;
4630         }
4631         else {
4632             var s = p / (2 * Math.PI) * Math.asin(c / a);
4633         }
4634
4635         if (t < 1) {
4636             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4637                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4638         }
4639         return a * Math.pow(2, -10 * (t -= 1)) *
4640                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4641     },
4642
4643
4644
4645     backIn: function (t, b, c, d, s) {
4646         if (typeof s == 'undefined') {
4647             s = 1.70158;
4648         }
4649         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4650     },
4651
4652
4653     backOut: function (t, b, c, d, s) {
4654         if (typeof s == 'undefined') {
4655             s = 1.70158;
4656         }
4657         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4658     },
4659
4660
4661     backBoth: function (t, b, c, d, s) {
4662         if (typeof s == 'undefined') {
4663             s = 1.70158;
4664         }
4665
4666         if ((t /= d / 2 ) < 1) {
4667             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4668         }
4669         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4670     },
4671
4672
4673     bounceIn: function (t, b, c, d) {
4674         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4675     },
4676
4677
4678     bounceOut: function (t, b, c, d) {
4679         if ((t /= d) < (1 / 2.75)) {
4680             return c * (7.5625 * t * t) + b;
4681         } else if (t < (2 / 2.75)) {
4682             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4683         } else if (t < (2.5 / 2.75)) {
4684             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4685         }
4686         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4687     },
4688
4689
4690     bounceBoth: function (t, b, c, d) {
4691         if (t < d / 2) {
4692             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4693         }
4694         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4695     }
4696 };/*
4697  * Portions of this file are based on pieces of Yahoo User Interface Library
4698  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4699  * YUI licensed under the BSD License:
4700  * http://developer.yahoo.net/yui/license.txt
4701  * <script type="text/javascript">
4702  *
4703  */
4704     (function() {
4705         Roo.lib.Motion = function(el, attributes, duration, method) {
4706             if (el) {
4707                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4708             }
4709         };
4710
4711         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4712
4713
4714         var Y = Roo.lib;
4715         var superclass = Y.Motion.superclass;
4716         var proto = Y.Motion.prototype;
4717
4718         proto.toString = function() {
4719             var el = this.getEl();
4720             var id = el.id || el.tagName;
4721             return ("Motion " + id);
4722         };
4723
4724         proto.patterns.points = /^points$/i;
4725
4726         proto.setAttribute = function(attr, val, unit) {
4727             if (this.patterns.points.test(attr)) {
4728                 unit = unit || 'px';
4729                 superclass.setAttribute.call(this, 'left', val[0], unit);
4730                 superclass.setAttribute.call(this, 'top', val[1], unit);
4731             } else {
4732                 superclass.setAttribute.call(this, attr, val, unit);
4733             }
4734         };
4735
4736         proto.getAttribute = function(attr) {
4737             if (this.patterns.points.test(attr)) {
4738                 var val = [
4739                         superclass.getAttribute.call(this, 'left'),
4740                         superclass.getAttribute.call(this, 'top')
4741                         ];
4742             } else {
4743                 val = superclass.getAttribute.call(this, attr);
4744             }
4745
4746             return val;
4747         };
4748
4749         proto.doMethod = function(attr, start, end) {
4750             var val = null;
4751
4752             if (this.patterns.points.test(attr)) {
4753                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4754                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4755             } else {
4756                 val = superclass.doMethod.call(this, attr, start, end);
4757             }
4758             return val;
4759         };
4760
4761         proto.setRuntimeAttribute = function(attr) {
4762             if (this.patterns.points.test(attr)) {
4763                 var el = this.getEl();
4764                 var attributes = this.attributes;
4765                 var start;
4766                 var control = attributes['points']['control'] || [];
4767                 var end;
4768                 var i, len;
4769
4770                 if (control.length > 0 && !(control[0] instanceof Array)) {
4771                     control = [control];
4772                 } else {
4773                     var tmp = [];
4774                     for (i = 0,len = control.length; i < len; ++i) {
4775                         tmp[i] = control[i];
4776                     }
4777                     control = tmp;
4778                 }
4779
4780                 Roo.fly(el).position();
4781
4782                 if (isset(attributes['points']['from'])) {
4783                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4784                 }
4785                 else {
4786                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4787                 }
4788
4789                 start = this.getAttribute('points');
4790
4791
4792                 if (isset(attributes['points']['to'])) {
4793                     end = translateValues.call(this, attributes['points']['to'], start);
4794
4795                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4796                     for (i = 0,len = control.length; i < len; ++i) {
4797                         control[i] = translateValues.call(this, control[i], start);
4798                     }
4799
4800
4801                 } else if (isset(attributes['points']['by'])) {
4802                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4803
4804                     for (i = 0,len = control.length; i < len; ++i) {
4805                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4806                     }
4807                 }
4808
4809                 this.runtimeAttributes[attr] = [start];
4810
4811                 if (control.length > 0) {
4812                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4813                 }
4814
4815                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4816             }
4817             else {
4818                 superclass.setRuntimeAttribute.call(this, attr);
4819             }
4820         };
4821
4822         var translateValues = function(val, start) {
4823             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4824             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4825
4826             return val;
4827         };
4828
4829         var isset = function(prop) {
4830             return (typeof prop !== 'undefined');
4831         };
4832     })();
4833 /*
4834  * Portions of this file are based on pieces of Yahoo User Interface Library
4835  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4836  * YUI licensed under the BSD License:
4837  * http://developer.yahoo.net/yui/license.txt
4838  * <script type="text/javascript">
4839  *
4840  */
4841     (function() {
4842         Roo.lib.Scroll = function(el, attributes, duration, method) {
4843             if (el) {
4844                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4845             }
4846         };
4847
4848         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4849
4850
4851         var Y = Roo.lib;
4852         var superclass = Y.Scroll.superclass;
4853         var proto = Y.Scroll.prototype;
4854
4855         proto.toString = function() {
4856             var el = this.getEl();
4857             var id = el.id || el.tagName;
4858             return ("Scroll " + id);
4859         };
4860
4861         proto.doMethod = function(attr, start, end) {
4862             var val = null;
4863
4864             if (attr == 'scroll') {
4865                 val = [
4866                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4867                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4868                         ];
4869
4870             } else {
4871                 val = superclass.doMethod.call(this, attr, start, end);
4872             }
4873             return val;
4874         };
4875
4876         proto.getAttribute = function(attr) {
4877             var val = null;
4878             var el = this.getEl();
4879
4880             if (attr == 'scroll') {
4881                 val = [ el.scrollLeft, el.scrollTop ];
4882             } else {
4883                 val = superclass.getAttribute.call(this, attr);
4884             }
4885
4886             return val;
4887         };
4888
4889         proto.setAttribute = function(attr, val, unit) {
4890             var el = this.getEl();
4891
4892             if (attr == 'scroll') {
4893                 el.scrollLeft = val[0];
4894                 el.scrollTop = val[1];
4895             } else {
4896                 superclass.setAttribute.call(this, attr, val, unit);
4897             }
4898         };
4899     })();
4900 /*
4901  * Based on:
4902  * Ext JS Library 1.1.1
4903  * Copyright(c) 2006-2007, Ext JS, LLC.
4904  *
4905  * Originally Released Under LGPL - original licence link has changed is not relivant.
4906  *
4907  * Fork - LGPL
4908  * <script type="text/javascript">
4909  */
4910
4911
4912 // nasty IE9 hack - what a pile of crap that is..
4913
4914  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4915     Range.prototype.createContextualFragment = function (html) {
4916         var doc = window.document;
4917         var container = doc.createElement("div");
4918         container.innerHTML = html;
4919         var frag = doc.createDocumentFragment(), n;
4920         while ((n = container.firstChild)) {
4921             frag.appendChild(n);
4922         }
4923         return frag;
4924     };
4925 }
4926
4927 /**
4928  * @class Roo.DomHelper
4929  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4930  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4931  * @static
4932  */
4933 Roo.DomHelper = function(){
4934     var tempTableEl = null;
4935     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4936     var tableRe = /^table|tbody|tr|td$/i;
4937     var xmlns = {};
4938     // build as innerHTML where available
4939     /** @ignore */
4940     var createHtml = function(o){
4941         if(typeof o == 'string'){
4942             return o;
4943         }
4944         var b = "";
4945         if(!o.tag){
4946             o.tag = "div";
4947         }
4948         b += "<" + o.tag;
4949         for(var attr in o){
4950             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4951             if(attr == "style"){
4952                 var s = o["style"];
4953                 if(typeof s == "function"){
4954                     s = s.call();
4955                 }
4956                 if(typeof s == "string"){
4957                     b += ' style="' + s + '"';
4958                 }else if(typeof s == "object"){
4959                     b += ' style="';
4960                     for(var key in s){
4961                         if(typeof s[key] != "function"){
4962                             b += key + ":" + s[key] + ";";
4963                         }
4964                     }
4965                     b += '"';
4966                 }
4967             }else{
4968                 if(attr == "cls"){
4969                     b += ' class="' + o["cls"] + '"';
4970                 }else if(attr == "htmlFor"){
4971                     b += ' for="' + o["htmlFor"] + '"';
4972                 }else{
4973                     b += " " + attr + '="' + o[attr] + '"';
4974                 }
4975             }
4976         }
4977         if(emptyTags.test(o.tag)){
4978             b += "/>";
4979         }else{
4980             b += ">";
4981             var cn = o.children || o.cn;
4982             if(cn){
4983                 //http://bugs.kde.org/show_bug.cgi?id=71506
4984                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4985                     for(var i = 0, len = cn.length; i < len; i++) {
4986                         b += createHtml(cn[i], b);
4987                     }
4988                 }else{
4989                     b += createHtml(cn, b);
4990                 }
4991             }
4992             if(o.html){
4993                 b += o.html;
4994             }
4995             b += "</" + o.tag + ">";
4996         }
4997         return b;
4998     };
4999
5000     // build as dom
5001     /** @ignore */
5002     var createDom = function(o, parentNode){
5003          
5004         // defininition craeted..
5005         var ns = false;
5006         if (o.ns && o.ns != 'html') {
5007                
5008             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5009                 xmlns[o.ns] = o.xmlns;
5010                 ns = o.xmlns;
5011             }
5012             if (typeof(xmlns[o.ns]) == 'undefined') {
5013                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5014             }
5015             ns = xmlns[o.ns];
5016         }
5017         
5018         
5019         if (typeof(o) == 'string') {
5020             return parentNode.appendChild(document.createTextNode(o));
5021         }
5022         o.tag = o.tag || div;
5023         if (o.ns && Roo.isIE) {
5024             ns = false;
5025             o.tag = o.ns + ':' + o.tag;
5026             
5027         }
5028         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5029         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5030         for(var attr in o){
5031             
5032             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5033                     attr == "style" || typeof o[attr] == "function") { continue; }
5034                     
5035             if(attr=="cls" && Roo.isIE){
5036                 el.className = o["cls"];
5037             }else{
5038                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5039                 else { 
5040                     el[attr] = o[attr];
5041                 }
5042             }
5043         }
5044         Roo.DomHelper.applyStyles(el, o.style);
5045         var cn = o.children || o.cn;
5046         if(cn){
5047             //http://bugs.kde.org/show_bug.cgi?id=71506
5048              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5049                 for(var i = 0, len = cn.length; i < len; i++) {
5050                     createDom(cn[i], el);
5051                 }
5052             }else{
5053                 createDom(cn, el);
5054             }
5055         }
5056         if(o.html){
5057             el.innerHTML = o.html;
5058         }
5059         if(parentNode){
5060            parentNode.appendChild(el);
5061         }
5062         return el;
5063     };
5064
5065     var ieTable = function(depth, s, h, e){
5066         tempTableEl.innerHTML = [s, h, e].join('');
5067         var i = -1, el = tempTableEl;
5068         while(++i < depth && el.firstChild){
5069             el = el.firstChild;
5070         }
5071         return el;
5072     };
5073
5074     // kill repeat to save bytes
5075     var ts = '<table>',
5076         te = '</table>',
5077         tbs = ts+'<tbody>',
5078         tbe = '</tbody>'+te,
5079         trs = tbs + '<tr>',
5080         tre = '</tr>'+tbe;
5081
5082     /**
5083      * @ignore
5084      * Nasty code for IE's broken table implementation
5085      */
5086     var insertIntoTable = function(tag, where, el, html){
5087         if(!tempTableEl){
5088             tempTableEl = document.createElement('div');
5089         }
5090         var node;
5091         var before = null;
5092         if(tag == 'td'){
5093             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5094                 return;
5095             }
5096             if(where == 'beforebegin'){
5097                 before = el;
5098                 el = el.parentNode;
5099             } else{
5100                 before = el.nextSibling;
5101                 el = el.parentNode;
5102             }
5103             node = ieTable(4, trs, html, tre);
5104         }
5105         else if(tag == 'tr'){
5106             if(where == 'beforebegin'){
5107                 before = el;
5108                 el = el.parentNode;
5109                 node = ieTable(3, tbs, html, tbe);
5110             } else if(where == 'afterend'){
5111                 before = el.nextSibling;
5112                 el = el.parentNode;
5113                 node = ieTable(3, tbs, html, tbe);
5114             } else{ // INTO a TR
5115                 if(where == 'afterbegin'){
5116                     before = el.firstChild;
5117                 }
5118                 node = ieTable(4, trs, html, tre);
5119             }
5120         } else if(tag == 'tbody'){
5121             if(where == 'beforebegin'){
5122                 before = el;
5123                 el = el.parentNode;
5124                 node = ieTable(2, ts, html, te);
5125             } else if(where == 'afterend'){
5126                 before = el.nextSibling;
5127                 el = el.parentNode;
5128                 node = ieTable(2, ts, html, te);
5129             } else{
5130                 if(where == 'afterbegin'){
5131                     before = el.firstChild;
5132                 }
5133                 node = ieTable(3, tbs, html, tbe);
5134             }
5135         } else{ // TABLE
5136             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5137                 return;
5138             }
5139             if(where == 'afterbegin'){
5140                 before = el.firstChild;
5141             }
5142             node = ieTable(2, ts, html, te);
5143         }
5144         el.insertBefore(node, before);
5145         return node;
5146     };
5147     
5148     // this is a bit like the react update code...
5149     // 
5150     
5151     var updateNode = function(from, to)
5152     {
5153         // should we handle non-standard elements?
5154         Roo.log(["UpdateNode" , from, to]);
5155         if (from.nodeType != to.nodeType) {
5156             Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5157             from.parentNode.replaceChild(to, from);
5158         }
5159         
5160         if (from.nodeType == 3) {
5161             // assume it's text?!
5162             if (from.data == to.data) {
5163                 return;
5164             }
5165             from.data = to.data;
5166             return;
5167         }
5168         
5169         // assume 'to' doesnt have '1/3 nodetypes!
5170         if (from.nodeType !=1 || from.tagName != to.tagName) {
5171             Roo.log(["ReplaceChild" , from, to ]);
5172             from.parentNode.replaceChild(to, from);
5173             return;
5174         }
5175         // compare attributes
5176         var ar = Array.from(from.attributes);
5177         for(var i = 0; i< ar.length;i++) {
5178             if (to.hasAttribute(ar[i].name)) {
5179                 continue;
5180             }
5181             if (ar[i].name == 'id') { // always keep ids?
5182                 continue;
5183             }
5184             from.removeAttribute(ar[i].name);
5185         }
5186         ar = to.attributes;
5187         for(var i = 0; i< ar.length;i++) {
5188             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5189                 continue;
5190             }
5191             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5192         }
5193         // children
5194         var far = Array.from(from.childNodes);
5195         var tar = Array.from(to.childNodes);
5196         // if the lengths are different.. then it's probably a editable content change, rather than
5197         // a change of the block definition..
5198         
5199         // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5200          /*if (from.innerHTML == to.innerHTML) {
5201             return;
5202         }
5203         if (far.length != tar.length) {
5204             from.innerHTML = to.innerHTML;
5205             return;
5206         }
5207         */
5208         
5209         for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5210             if (i >= far.length) {
5211                 from.appendChild(tar[i]);
5212                 Roo.log(["add", tar[i]]);
5213                 
5214             } else if ( i  >= tar.length) {
5215                 from.removeChild(far[i]);
5216                 Roo.log(["remove", far[i]]);
5217             } else {
5218                 
5219                 updateNode(far[i], tar[i]);
5220             }    
5221         }
5222         
5223         
5224         
5225         
5226     };
5227     
5228     
5229
5230     return {
5231         /** True to force the use of DOM instead of html fragments @type Boolean */
5232         useDom : false,
5233     
5234         /**
5235          * Returns the markup for the passed Element(s) config
5236          * @param {Object} o The Dom object spec (and children)
5237          * @return {String}
5238          */
5239         markup : function(o){
5240             return createHtml(o);
5241         },
5242     
5243         /**
5244          * Applies a style specification to an element
5245          * @param {String/HTMLElement} el The element to apply styles to
5246          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5247          * a function which returns such a specification.
5248          */
5249         applyStyles : function(el, styles){
5250             if(styles){
5251                el = Roo.fly(el);
5252                if(typeof styles == "string"){
5253                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5254                    var matches;
5255                    while ((matches = re.exec(styles)) != null){
5256                        el.setStyle(matches[1], matches[2]);
5257                    }
5258                }else if (typeof styles == "object"){
5259                    for (var style in styles){
5260                       el.setStyle(style, styles[style]);
5261                    }
5262                }else if (typeof styles == "function"){
5263                     Roo.DomHelper.applyStyles(el, styles.call());
5264                }
5265             }
5266         },
5267     
5268         /**
5269          * Inserts an HTML fragment into the Dom
5270          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5271          * @param {HTMLElement} el The context element
5272          * @param {String} html The HTML fragmenet
5273          * @return {HTMLElement} The new node
5274          */
5275         insertHtml : function(where, el, html){
5276             where = where.toLowerCase();
5277             if(el.insertAdjacentHTML){
5278                 if(tableRe.test(el.tagName)){
5279                     var rs;
5280                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5281                         return rs;
5282                     }
5283                 }
5284                 switch(where){
5285                     case "beforebegin":
5286                         el.insertAdjacentHTML('BeforeBegin', html);
5287                         return el.previousSibling;
5288                     case "afterbegin":
5289                         el.insertAdjacentHTML('AfterBegin', html);
5290                         return el.firstChild;
5291                     case "beforeend":
5292                         el.insertAdjacentHTML('BeforeEnd', html);
5293                         return el.lastChild;
5294                     case "afterend":
5295                         el.insertAdjacentHTML('AfterEnd', html);
5296                         return el.nextSibling;
5297                 }
5298                 throw 'Illegal insertion point -> "' + where + '"';
5299             }
5300             var range = el.ownerDocument.createRange();
5301             var frag;
5302             switch(where){
5303                  case "beforebegin":
5304                     range.setStartBefore(el);
5305                     frag = range.createContextualFragment(html);
5306                     el.parentNode.insertBefore(frag, el);
5307                     return el.previousSibling;
5308                  case "afterbegin":
5309                     if(el.firstChild){
5310                         range.setStartBefore(el.firstChild);
5311                         frag = range.createContextualFragment(html);
5312                         el.insertBefore(frag, el.firstChild);
5313                         return el.firstChild;
5314                     }else{
5315                         el.innerHTML = html;
5316                         return el.firstChild;
5317                     }
5318                 case "beforeend":
5319                     if(el.lastChild){
5320                         range.setStartAfter(el.lastChild);
5321                         frag = range.createContextualFragment(html);
5322                         el.appendChild(frag);
5323                         return el.lastChild;
5324                     }else{
5325                         el.innerHTML = html;
5326                         return el.lastChild;
5327                     }
5328                 case "afterend":
5329                     range.setStartAfter(el);
5330                     frag = range.createContextualFragment(html);
5331                     el.parentNode.insertBefore(frag, el.nextSibling);
5332                     return el.nextSibling;
5333                 }
5334                 throw 'Illegal insertion point -> "' + where + '"';
5335         },
5336     
5337         /**
5338          * Creates new Dom element(s) and inserts them before el
5339          * @param {String/HTMLElement/Element} el The context element
5340          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5341          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5342          * @return {HTMLElement/Roo.Element} The new node
5343          */
5344         insertBefore : function(el, o, returnElement){
5345             return this.doInsert(el, o, returnElement, "beforeBegin");
5346         },
5347     
5348         /**
5349          * Creates new Dom element(s) and inserts them after el
5350          * @param {String/HTMLElement/Element} el The context element
5351          * @param {Object} o The Dom object spec (and children)
5352          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5353          * @return {HTMLElement/Roo.Element} The new node
5354          */
5355         insertAfter : function(el, o, returnElement){
5356             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5357         },
5358     
5359         /**
5360          * Creates new Dom element(s) and inserts them as the first child of el
5361          * @param {String/HTMLElement/Element} el The context element
5362          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5363          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5364          * @return {HTMLElement/Roo.Element} The new node
5365          */
5366         insertFirst : function(el, o, returnElement){
5367             return this.doInsert(el, o, returnElement, "afterBegin");
5368         },
5369     
5370         // private
5371         doInsert : function(el, o, returnElement, pos, sibling){
5372             el = Roo.getDom(el);
5373             var newNode;
5374             if(this.useDom || o.ns){
5375                 newNode = createDom(o, null);
5376                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5377             }else{
5378                 var html = createHtml(o);
5379                 newNode = this.insertHtml(pos, el, html);
5380             }
5381             return returnElement ? Roo.get(newNode, true) : newNode;
5382         },
5383     
5384         /**
5385          * Creates new Dom element(s) and appends them to el
5386          * @param {String/HTMLElement/Element} el The context element
5387          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5388          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5389          * @return {HTMLElement/Roo.Element} The new node
5390          */
5391         append : function(el, o, returnElement){
5392             el = Roo.getDom(el);
5393             var newNode;
5394             if(this.useDom || o.ns){
5395                 newNode = createDom(o, null);
5396                 el.appendChild(newNode);
5397             }else{
5398                 var html = createHtml(o);
5399                 newNode = this.insertHtml("beforeEnd", el, html);
5400             }
5401             return returnElement ? Roo.get(newNode, true) : newNode;
5402         },
5403     
5404         /**
5405          * Creates new Dom element(s) and overwrites the contents of el with them
5406          * @param {String/HTMLElement/Element} el The context element
5407          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5408          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5409          * @return {HTMLElement/Roo.Element} The new node
5410          */
5411         overwrite : function(el, o, returnElement)
5412         {
5413             el = Roo.getDom(el);
5414             if (o.ns) {
5415               
5416                 while (el.childNodes.length) {
5417                     el.removeChild(el.firstChild);
5418                 }
5419                 createDom(o, el);
5420             } else {
5421                 el.innerHTML = createHtml(o);   
5422             }
5423             
5424             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5425         },
5426     
5427         /**
5428          * Creates a new Roo.DomHelper.Template from the Dom object spec
5429          * @param {Object} o The Dom object spec (and children)
5430          * @return {Roo.DomHelper.Template} The new template
5431          */
5432         createTemplate : function(o){
5433             var html = createHtml(o);
5434             return new Roo.Template(html);
5435         },
5436          /**
5437          * Updates the first element with the spec from the o (replacing if necessary)
5438          * This iterates through the children, and updates attributes / children etc..
5439          * @param {String/HTMLElement/Element} el The context element
5440          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5441          */
5442         
5443         update : function(el, o)
5444         {
5445             updateNode(Roo.getDom(el), createDom(o));
5446             
5447         }
5448         
5449         
5450     };
5451 }();
5452 /*
5453  * Based on:
5454  * Ext JS Library 1.1.1
5455  * Copyright(c) 2006-2007, Ext JS, LLC.
5456  *
5457  * Originally Released Under LGPL - original licence link has changed is not relivant.
5458  *
5459  * Fork - LGPL
5460  * <script type="text/javascript">
5461  */
5462  
5463 /**
5464 * @class Roo.Template
5465 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5466 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5467 * Usage:
5468 <pre><code>
5469 var t = new Roo.Template({
5470     html :  '&lt;div name="{id}"&gt;' + 
5471         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5472         '&lt;/div&gt;',
5473     myformat: function (value, allValues) {
5474         return 'XX' + value;
5475     }
5476 });
5477 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5478 </code></pre>
5479 * For more information see this blog post with examples:
5480 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5481      - Create Elements using DOM, HTML fragments and Templates</a>. 
5482 * @constructor
5483 * @param {Object} cfg - Configuration object.
5484 */
5485 Roo.Template = function(cfg){
5486     // BC!
5487     if(cfg instanceof Array){
5488         cfg = cfg.join("");
5489     }else if(arguments.length > 1){
5490         cfg = Array.prototype.join.call(arguments, "");
5491     }
5492     
5493     
5494     if (typeof(cfg) == 'object') {
5495         Roo.apply(this,cfg)
5496     } else {
5497         // bc
5498         this.html = cfg;
5499     }
5500     if (this.url) {
5501         this.load();
5502     }
5503     
5504 };
5505 Roo.Template.prototype = {
5506     
5507     /**
5508      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5509      */
5510     onLoad : false,
5511     
5512     
5513     /**
5514      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
5515      *                    it should be fixed so that template is observable...
5516      */
5517     url : false,
5518     /**
5519      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5520      */
5521     html : '',
5522     
5523     
5524     compiled : false,
5525     loaded : false,
5526     /**
5527      * Returns an HTML fragment of this template with the specified values applied.
5528      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
5529      * @return {String} The HTML fragment
5530      */
5531     
5532    
5533     
5534     applyTemplate : function(values){
5535         //Roo.log(["applyTemplate", values]);
5536         try {
5537            
5538             if(this.compiled){
5539                 return this.compiled(values);
5540             }
5541             var useF = this.disableFormats !== true;
5542             var fm = Roo.util.Format, tpl = this;
5543             var fn = function(m, name, format, args){
5544                 if(format && useF){
5545                     if(format.substr(0, 5) == "this."){
5546                         return tpl.call(format.substr(5), values[name], values);
5547                     }else{
5548                         if(args){
5549                             // quoted values are required for strings in compiled templates, 
5550                             // but for non compiled we need to strip them
5551                             // quoted reversed for jsmin
5552                             var re = /^\s*['"](.*)["']\s*$/;
5553                             args = args.split(',');
5554                             for(var i = 0, len = args.length; i < len; i++){
5555                                 args[i] = args[i].replace(re, "$1");
5556                             }
5557                             args = [values[name]].concat(args);
5558                         }else{
5559                             args = [values[name]];
5560                         }
5561                         return fm[format].apply(fm, args);
5562                     }
5563                 }else{
5564                     return values[name] !== undefined ? values[name] : "";
5565                 }
5566             };
5567             return this.html.replace(this.re, fn);
5568         } catch (e) {
5569             Roo.log(e);
5570             throw e;
5571         }
5572          
5573     },
5574     
5575     loading : false,
5576       
5577     load : function ()
5578     {
5579          
5580         if (this.loading) {
5581             return;
5582         }
5583         var _t = this;
5584         
5585         this.loading = true;
5586         this.compiled = false;
5587         
5588         var cx = new Roo.data.Connection();
5589         cx.request({
5590             url : this.url,
5591             method : 'GET',
5592             success : function (response) {
5593                 _t.loading = false;
5594                 _t.url = false;
5595                 
5596                 _t.set(response.responseText,true);
5597                 _t.loaded = true;
5598                 if (_t.onLoad) {
5599                     _t.onLoad();
5600                 }
5601              },
5602             failure : function(response) {
5603                 Roo.log("Template failed to load from " + _t.url);
5604                 _t.loading = false;
5605             }
5606         });
5607     },
5608
5609     /**
5610      * Sets the HTML used as the template and optionally compiles it.
5611      * @param {String} html
5612      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5613      * @return {Roo.Template} this
5614      */
5615     set : function(html, compile){
5616         this.html = html;
5617         this.compiled = false;
5618         if(compile){
5619             this.compile();
5620         }
5621         return this;
5622     },
5623     
5624     /**
5625      * True to disable format functions (defaults to false)
5626      * @type Boolean
5627      */
5628     disableFormats : false,
5629     
5630     /**
5631     * The regular expression used to match template variables 
5632     * @type RegExp
5633     * @property 
5634     */
5635     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5636     
5637     /**
5638      * Compiles the template into an internal function, eliminating the RegEx overhead.
5639      * @return {Roo.Template} this
5640      */
5641     compile : function(){
5642         var fm = Roo.util.Format;
5643         var useF = this.disableFormats !== true;
5644         var sep = Roo.isGecko ? "+" : ",";
5645         var fn = function(m, name, format, args){
5646             if(format && useF){
5647                 args = args ? ',' + args : "";
5648                 if(format.substr(0, 5) != "this."){
5649                     format = "fm." + format + '(';
5650                 }else{
5651                     format = 'this.call("'+ format.substr(5) + '", ';
5652                     args = ", values";
5653                 }
5654             }else{
5655                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5656             }
5657             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5658         };
5659         var body;
5660         // branched to use + in gecko and [].join() in others
5661         if(Roo.isGecko){
5662             body = "this.compiled = function(values){ return '" +
5663                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5664                     "';};";
5665         }else{
5666             body = ["this.compiled = function(values){ return ['"];
5667             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5668             body.push("'].join('');};");
5669             body = body.join('');
5670         }
5671         /**
5672          * eval:var:values
5673          * eval:var:fm
5674          */
5675         eval(body);
5676         return this;
5677     },
5678     
5679     // private function used to call members
5680     call : function(fnName, value, allValues){
5681         return this[fnName](value, allValues);
5682     },
5683     
5684     /**
5685      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
5686      * @param {String/HTMLElement/Roo.Element} el The context element
5687      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
5688      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5689      * @return {HTMLElement/Roo.Element} The new node or Element
5690      */
5691     insertFirst: function(el, values, returnElement){
5692         return this.doInsert('afterBegin', el, values, returnElement);
5693     },
5694
5695     /**
5696      * Applies the supplied values to the template and inserts the new node(s) before el.
5697      * @param {String/HTMLElement/Roo.Element} el The context element
5698      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
5699      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5700      * @return {HTMLElement/Roo.Element} The new node or Element
5701      */
5702     insertBefore: function(el, values, returnElement){
5703         return this.doInsert('beforeBegin', el, values, returnElement);
5704     },
5705
5706     /**
5707      * Applies the supplied values to the template and inserts the new node(s) after el.
5708      * @param {String/HTMLElement/Roo.Element} el The context element
5709      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
5710      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5711      * @return {HTMLElement/Roo.Element} The new node or Element
5712      */
5713     insertAfter : function(el, values, returnElement){
5714         return this.doInsert('afterEnd', el, values, returnElement);
5715     },
5716     
5717     /**
5718      * Applies the supplied values to the template and appends the new node(s) to el.
5719      * @param {String/HTMLElement/Roo.Element} el The context element
5720      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
5721      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5722      * @return {HTMLElement/Roo.Element} The new node or Element
5723      */
5724     append : function(el, values, returnElement){
5725         return this.doInsert('beforeEnd', el, values, returnElement);
5726     },
5727
5728     doInsert : function(where, el, values, returnEl){
5729         el = Roo.getDom(el);
5730         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
5731         return returnEl ? Roo.get(newNode, true) : newNode;
5732     },
5733
5734     /**
5735      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
5736      * @param {String/HTMLElement/Roo.Element} el The context element
5737      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
5738      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5739      * @return {HTMLElement/Roo.Element} The new node or Element
5740      */
5741     overwrite : function(el, values, returnElement){
5742         el = Roo.getDom(el);
5743         el.innerHTML = this.applyTemplate(values);
5744         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5745     }
5746 };
5747 /**
5748  * Alias for {@link #applyTemplate}
5749  * @method
5750  */
5751 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
5752
5753 // backwards compat
5754 Roo.DomHelper.Template = Roo.Template;
5755
5756 /**
5757  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
5758  * @param {String/HTMLElement} el A DOM element or its id
5759  * @returns {Roo.Template} The created template
5760  * @static
5761  */
5762 Roo.Template.from = function(el){
5763     el = Roo.getDom(el);
5764     return new Roo.Template(el.value || el.innerHTML);
5765 };/*
5766  * Based on:
5767  * Ext JS Library 1.1.1
5768  * Copyright(c) 2006-2007, Ext JS, LLC.
5769  *
5770  * Originally Released Under LGPL - original licence link has changed is not relivant.
5771  *
5772  * Fork - LGPL
5773  * <script type="text/javascript">
5774  */
5775  
5776
5777 /*
5778  * This is code is also distributed under MIT license for use
5779  * with jQuery and prototype JavaScript libraries.
5780  */
5781 /**
5782  * @class Roo.DomQuery
5783 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
5784 <p>
5785 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
5786
5787 <p>
5788 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
5789 </p>
5790 <h4>Element Selectors:</h4>
5791 <ul class="list">
5792     <li> <b>*</b> any element</li>
5793     <li> <b>E</b> an element with the tag E</li>
5794     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
5795     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
5796     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
5797     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
5798 </ul>
5799 <h4>Attribute Selectors:</h4>
5800 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
5801 <ul class="list">
5802     <li> <b>E[foo]</b> has an attribute "foo"</li>
5803     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
5804     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
5805     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
5806     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
5807     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
5808     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
5809 </ul>
5810 <h4>Pseudo Classes:</h4>
5811 <ul class="list">
5812     <li> <b>E:first-child</b> E is the first child of its parent</li>
5813     <li> <b>E:last-child</b> E is the last child of its parent</li>
5814     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
5815     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
5816     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
5817     <li> <b>E:only-child</b> E is the only child of its parent</li>
5818     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
5819     <li> <b>E:first</b> the first E in the resultset</li>
5820     <li> <b>E:last</b> the last E in the resultset</li>
5821     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
5822     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
5823     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
5824     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
5825     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5826     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5827     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5828     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5829     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5830 </ul>
5831 <h4>CSS Value Selectors:</h4>
5832 <ul class="list">
5833     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5834     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5835     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5836     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5837     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5838     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5839 </ul>
5840  * @static
5841  */
5842 Roo.DomQuery = function(){
5843     var cache = {}, simpleCache = {}, valueCache = {};
5844     var nonSpace = /\S/;
5845     var trimRe = /^\s+|\s+$/g;
5846     var tplRe = /\{(\d+)\}/g;
5847     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5848     var tagTokenRe = /^(#)?([\w-\*]+)/;
5849     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5850
5851     function child(p, index){
5852         var i = 0;
5853         var n = p.firstChild;
5854         while(n){
5855             if(n.nodeType == 1){
5856                if(++i == index){
5857                    return n;
5858                }
5859             }
5860             n = n.nextSibling;
5861         }
5862         return null;
5863     };
5864
5865     function next(n){
5866         while((n = n.nextSibling) && n.nodeType != 1);
5867         return n;
5868     };
5869
5870     function prev(n){
5871         while((n = n.previousSibling) && n.nodeType != 1);
5872         return n;
5873     };
5874
5875     function children(d){
5876         var n = d.firstChild, ni = -1;
5877             while(n){
5878                 var nx = n.nextSibling;
5879                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5880                     d.removeChild(n);
5881                 }else{
5882                     n.nodeIndex = ++ni;
5883                 }
5884                 n = nx;
5885             }
5886             return this;
5887         };
5888
5889     function byClassName(c, a, v){
5890         if(!v){
5891             return c;
5892         }
5893         var r = [], ri = -1, cn;
5894         for(var i = 0, ci; ci = c[i]; i++){
5895             
5896             
5897             if((' '+
5898                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
5899                  +' ').indexOf(v) != -1){
5900                 r[++ri] = ci;
5901             }
5902         }
5903         return r;
5904     };
5905
5906     function attrValue(n, attr){
5907         if(!n.tagName && typeof n.length != "undefined"){
5908             n = n[0];
5909         }
5910         if(!n){
5911             return null;
5912         }
5913         if(attr == "for"){
5914             return n.htmlFor;
5915         }
5916         if(attr == "class" || attr == "className"){
5917             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
5918         }
5919         return n.getAttribute(attr) || n[attr];
5920
5921     };
5922
5923     function getNodes(ns, mode, tagName){
5924         var result = [], ri = -1, cs;
5925         if(!ns){
5926             return result;
5927         }
5928         tagName = tagName || "*";
5929         if(typeof ns.getElementsByTagName != "undefined"){
5930             ns = [ns];
5931         }
5932         if(!mode){
5933             for(var i = 0, ni; ni = ns[i]; i++){
5934                 cs = ni.getElementsByTagName(tagName);
5935                 for(var j = 0, ci; ci = cs[j]; j++){
5936                     result[++ri] = ci;
5937                 }
5938             }
5939         }else if(mode == "/" || mode == ">"){
5940             var utag = tagName.toUpperCase();
5941             for(var i = 0, ni, cn; ni = ns[i]; i++){
5942                 cn = ni.children || ni.childNodes;
5943                 for(var j = 0, cj; cj = cn[j]; j++){
5944                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5945                         result[++ri] = cj;
5946                     }
5947                 }
5948             }
5949         }else if(mode == "+"){
5950             var utag = tagName.toUpperCase();
5951             for(var i = 0, n; n = ns[i]; i++){
5952                 while((n = n.nextSibling) && n.nodeType != 1);
5953                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5954                     result[++ri] = n;
5955                 }
5956             }
5957         }else if(mode == "~"){
5958             for(var i = 0, n; n = ns[i]; i++){
5959                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5960                 if(n){
5961                     result[++ri] = n;
5962                 }
5963             }
5964         }
5965         return result;
5966     };
5967
5968     function concat(a, b){
5969         if(b.slice){
5970             return a.concat(b);
5971         }
5972         for(var i = 0, l = b.length; i < l; i++){
5973             a[a.length] = b[i];
5974         }
5975         return a;
5976     }
5977
5978     function byTag(cs, tagName){
5979         if(cs.tagName || cs == document){
5980             cs = [cs];
5981         }
5982         if(!tagName){
5983             return cs;
5984         }
5985         var r = [], ri = -1;
5986         tagName = tagName.toLowerCase();
5987         for(var i = 0, ci; ci = cs[i]; i++){
5988             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5989                 r[++ri] = ci;
5990             }
5991         }
5992         return r;
5993     };
5994
5995     function byId(cs, attr, id){
5996         if(cs.tagName || cs == document){
5997             cs = [cs];
5998         }
5999         if(!id){
6000             return cs;
6001         }
6002         var r = [], ri = -1;
6003         for(var i = 0,ci; ci = cs[i]; i++){
6004             if(ci && ci.id == id){
6005                 r[++ri] = ci;
6006                 return r;
6007             }
6008         }
6009         return r;
6010     };
6011
6012     function byAttribute(cs, attr, value, op, custom){
6013         var r = [], ri = -1, st = custom=="{";
6014         var f = Roo.DomQuery.operators[op];
6015         for(var i = 0, ci; ci = cs[i]; i++){
6016             var a;
6017             if(st){
6018                 a = Roo.DomQuery.getStyle(ci, attr);
6019             }
6020             else if(attr == "class" || attr == "className"){
6021                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6022             }else if(attr == "for"){
6023                 a = ci.htmlFor;
6024             }else if(attr == "href"){
6025                 a = ci.getAttribute("href", 2);
6026             }else{
6027                 a = ci.getAttribute(attr);
6028             }
6029             if((f && f(a, value)) || (!f && a)){
6030                 r[++ri] = ci;
6031             }
6032         }
6033         return r;
6034     };
6035
6036     function byPseudo(cs, name, value){
6037         return Roo.DomQuery.pseudos[name](cs, value);
6038     };
6039
6040     // This is for IE MSXML which does not support expandos.
6041     // IE runs the same speed using setAttribute, however FF slows way down
6042     // and Safari completely fails so they need to continue to use expandos.
6043     var isIE = window.ActiveXObject ? true : false;
6044
6045     // this eval is stop the compressor from
6046     // renaming the variable to something shorter
6047     
6048     /** eval:var:batch */
6049     var batch = 30803; 
6050
6051     var key = 30803;
6052
6053     function nodupIEXml(cs){
6054         var d = ++key;
6055         cs[0].setAttribute("_nodup", d);
6056         var r = [cs[0]];
6057         for(var i = 1, len = cs.length; i < len; i++){
6058             var c = cs[i];
6059             if(!c.getAttribute("_nodup") != d){
6060                 c.setAttribute("_nodup", d);
6061                 r[r.length] = c;
6062             }
6063         }
6064         for(var i = 0, len = cs.length; i < len; i++){
6065             cs[i].removeAttribute("_nodup");
6066         }
6067         return r;
6068     }
6069
6070     function nodup(cs){
6071         if(!cs){
6072             return [];
6073         }
6074         var len = cs.length, c, i, r = cs, cj, ri = -1;
6075         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6076             return cs;
6077         }
6078         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6079             return nodupIEXml(cs);
6080         }
6081         var d = ++key;
6082         cs[0]._nodup = d;
6083         for(i = 1; c = cs[i]; i++){
6084             if(c._nodup != d){
6085                 c._nodup = d;
6086             }else{
6087                 r = [];
6088                 for(var j = 0; j < i; j++){
6089                     r[++ri] = cs[j];
6090                 }
6091                 for(j = i+1; cj = cs[j]; j++){
6092                     if(cj._nodup != d){
6093                         cj._nodup = d;
6094                         r[++ri] = cj;
6095                     }
6096                 }
6097                 return r;
6098             }
6099         }
6100         return r;
6101     }
6102
6103     function quickDiffIEXml(c1, c2){
6104         var d = ++key;
6105         for(var i = 0, len = c1.length; i < len; i++){
6106             c1[i].setAttribute("_qdiff", d);
6107         }
6108         var r = [];
6109         for(var i = 0, len = c2.length; i < len; i++){
6110             if(c2[i].getAttribute("_qdiff") != d){
6111                 r[r.length] = c2[i];
6112             }
6113         }
6114         for(var i = 0, len = c1.length; i < len; i++){
6115            c1[i].removeAttribute("_qdiff");
6116         }
6117         return r;
6118     }
6119
6120     function quickDiff(c1, c2){
6121         var len1 = c1.length;
6122         if(!len1){
6123             return c2;
6124         }
6125         if(isIE && c1[0].selectSingleNode){
6126             return quickDiffIEXml(c1, c2);
6127         }
6128         var d = ++key;
6129         for(var i = 0; i < len1; i++){
6130             c1[i]._qdiff = d;
6131         }
6132         var r = [];
6133         for(var i = 0, len = c2.length; i < len; i++){
6134             if(c2[i]._qdiff != d){
6135                 r[r.length] = c2[i];
6136             }
6137         }
6138         return r;
6139     }
6140
6141     function quickId(ns, mode, root, id){
6142         if(ns == root){
6143            var d = root.ownerDocument || root;
6144            return d.getElementById(id);
6145         }
6146         ns = getNodes(ns, mode, "*");
6147         return byId(ns, null, id);
6148     }
6149
6150     return {
6151         getStyle : function(el, name){
6152             return Roo.fly(el).getStyle(name);
6153         },
6154         /**
6155          * Compiles a selector/xpath query into a reusable function. The returned function
6156          * takes one parameter "root" (optional), which is the context node from where the query should start.
6157          * @param {String} selector The selector/xpath query
6158          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6159          * @return {Function}
6160          */
6161         compile : function(path, type){
6162             type = type || "select";
6163             
6164             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6165             var q = path, mode, lq;
6166             var tk = Roo.DomQuery.matchers;
6167             var tklen = tk.length;
6168             var mm;
6169
6170             // accept leading mode switch
6171             var lmode = q.match(modeRe);
6172             if(lmode && lmode[1]){
6173                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6174                 q = q.replace(lmode[1], "");
6175             }
6176             // strip leading slashes
6177             while(path.substr(0, 1)=="/"){
6178                 path = path.substr(1);
6179             }
6180
6181             while(q && lq != q){
6182                 lq = q;
6183                 var tm = q.match(tagTokenRe);
6184                 if(type == "select"){
6185                     if(tm){
6186                         if(tm[1] == "#"){
6187                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6188                         }else{
6189                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6190                         }
6191                         q = q.replace(tm[0], "");
6192                     }else if(q.substr(0, 1) != '@'){
6193                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6194                     }
6195                 }else{
6196                     if(tm){
6197                         if(tm[1] == "#"){
6198                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6199                         }else{
6200                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6201                         }
6202                         q = q.replace(tm[0], "");
6203                     }
6204                 }
6205                 while(!(mm = q.match(modeRe))){
6206                     var matched = false;
6207                     for(var j = 0; j < tklen; j++){
6208                         var t = tk[j];
6209                         var m = q.match(t.re);
6210                         if(m){
6211                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6212                                                     return m[i];
6213                                                 });
6214                             q = q.replace(m[0], "");
6215                             matched = true;
6216                             break;
6217                         }
6218                     }
6219                     // prevent infinite loop on bad selector
6220                     if(!matched){
6221                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6222                     }
6223                 }
6224                 if(mm[1]){
6225                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6226                     q = q.replace(mm[1], "");
6227                 }
6228             }
6229             fn[fn.length] = "return nodup(n);\n}";
6230             
6231              /** 
6232               * list of variables that need from compression as they are used by eval.
6233              *  eval:var:batch 
6234              *  eval:var:nodup
6235              *  eval:var:byTag
6236              *  eval:var:ById
6237              *  eval:var:getNodes
6238              *  eval:var:quickId
6239              *  eval:var:mode
6240              *  eval:var:root
6241              *  eval:var:n
6242              *  eval:var:byClassName
6243              *  eval:var:byPseudo
6244              *  eval:var:byAttribute
6245              *  eval:var:attrValue
6246              * 
6247              **/ 
6248             eval(fn.join(""));
6249             return f;
6250         },
6251
6252         /**
6253          * Selects a group of elements.
6254          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6255          * @param {Node} root (optional) The start of the query (defaults to document).
6256          * @return {Array}
6257          */
6258         select : function(path, root, type){
6259             if(!root || root == document){
6260                 root = document;
6261             }
6262             if(typeof root == "string"){
6263                 root = document.getElementById(root);
6264             }
6265             var paths = path.split(",");
6266             var results = [];
6267             for(var i = 0, len = paths.length; i < len; i++){
6268                 var p = paths[i].replace(trimRe, "");
6269                 if(!cache[p]){
6270                     cache[p] = Roo.DomQuery.compile(p);
6271                     if(!cache[p]){
6272                         throw p + " is not a valid selector";
6273                     }
6274                 }
6275                 var result = cache[p](root);
6276                 if(result && result != document){
6277                     results = results.concat(result);
6278                 }
6279             }
6280             if(paths.length > 1){
6281                 return nodup(results);
6282             }
6283             return results;
6284         },
6285
6286         /**
6287          * Selects a single element.
6288          * @param {String} selector The selector/xpath query
6289          * @param {Node} root (optional) The start of the query (defaults to document).
6290          * @return {Element}
6291          */
6292         selectNode : function(path, root){
6293             return Roo.DomQuery.select(path, root)[0];
6294         },
6295
6296         /**
6297          * Selects the value of a node, optionally replacing null with the defaultValue.
6298          * @param {String} selector The selector/xpath query
6299          * @param {Node} root (optional) The start of the query (defaults to document).
6300          * @param {String} defaultValue
6301          */
6302         selectValue : function(path, root, defaultValue){
6303             path = path.replace(trimRe, "");
6304             if(!valueCache[path]){
6305                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6306             }
6307             var n = valueCache[path](root);
6308             n = n[0] ? n[0] : n;
6309             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6310             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6311         },
6312
6313         /**
6314          * Selects the value of a node, parsing integers and floats.
6315          * @param {String} selector The selector/xpath query
6316          * @param {Node} root (optional) The start of the query (defaults to document).
6317          * @param {Number} defaultValue
6318          * @return {Number}
6319          */
6320         selectNumber : function(path, root, defaultValue){
6321             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6322             return parseFloat(v);
6323         },
6324
6325         /**
6326          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6327          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6328          * @param {String} selector The simple selector to test
6329          * @return {Boolean}
6330          */
6331         is : function(el, ss){
6332             if(typeof el == "string"){
6333                 el = document.getElementById(el);
6334             }
6335             var isArray = (el instanceof Array);
6336             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6337             return isArray ? (result.length == el.length) : (result.length > 0);
6338         },
6339
6340         /**
6341          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6342          * @param {Array} el An array of elements to filter
6343          * @param {String} selector The simple selector to test
6344          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6345          * the selector instead of the ones that match
6346          * @return {Array}
6347          */
6348         filter : function(els, ss, nonMatches){
6349             ss = ss.replace(trimRe, "");
6350             if(!simpleCache[ss]){
6351                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6352             }
6353             var result = simpleCache[ss](els);
6354             return nonMatches ? quickDiff(result, els) : result;
6355         },
6356
6357         /**
6358          * Collection of matching regular expressions and code snippets.
6359          */
6360         matchers : [{
6361                 re: /^\.([\w-]+)/,
6362                 select: 'n = byClassName(n, null, " {1} ");'
6363             }, {
6364                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6365                 select: 'n = byPseudo(n, "{1}", "{2}");'
6366             },{
6367                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6368                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6369             }, {
6370                 re: /^#([\w-]+)/,
6371                 select: 'n = byId(n, null, "{1}");'
6372             },{
6373                 re: /^@([\w-]+)/,
6374                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6375             }
6376         ],
6377
6378         /**
6379          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6380          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
6381          */
6382         operators : {
6383             "=" : function(a, v){
6384                 return a == v;
6385             },
6386             "!=" : function(a, v){
6387                 return a != v;
6388             },
6389             "^=" : function(a, v){
6390                 return a && a.substr(0, v.length) == v;
6391             },
6392             "$=" : function(a, v){
6393                 return a && a.substr(a.length-v.length) == v;
6394             },
6395             "*=" : function(a, v){
6396                 return a && a.indexOf(v) !== -1;
6397             },
6398             "%=" : function(a, v){
6399                 return (a % v) == 0;
6400             },
6401             "|=" : function(a, v){
6402                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6403             },
6404             "~=" : function(a, v){
6405                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6406             }
6407         },
6408
6409         /**
6410          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6411          * and the argument (if any) supplied in the selector.
6412          */
6413         pseudos : {
6414             "first-child" : function(c){
6415                 var r = [], ri = -1, n;
6416                 for(var i = 0, ci; ci = n = c[i]; i++){
6417                     while((n = n.previousSibling) && n.nodeType != 1);
6418                     if(!n){
6419                         r[++ri] = ci;
6420                     }
6421                 }
6422                 return r;
6423             },
6424
6425             "last-child" : function(c){
6426                 var r = [], ri = -1, n;
6427                 for(var i = 0, ci; ci = n = c[i]; i++){
6428                     while((n = n.nextSibling) && n.nodeType != 1);
6429                     if(!n){
6430                         r[++ri] = ci;
6431                     }
6432                 }
6433                 return r;
6434             },
6435
6436             "nth-child" : function(c, a) {
6437                 var r = [], ri = -1;
6438                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6439                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6440                 for(var i = 0, n; n = c[i]; i++){
6441                     var pn = n.parentNode;
6442                     if (batch != pn._batch) {
6443                         var j = 0;
6444                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6445                             if(cn.nodeType == 1){
6446                                cn.nodeIndex = ++j;
6447                             }
6448                         }
6449                         pn._batch = batch;
6450                     }
6451                     if (f == 1) {
6452                         if (l == 0 || n.nodeIndex == l){
6453                             r[++ri] = n;
6454                         }
6455                     } else if ((n.nodeIndex + l) % f == 0){
6456                         r[++ri] = n;
6457                     }
6458                 }
6459
6460                 return r;
6461             },
6462
6463             "only-child" : function(c){
6464                 var r = [], ri = -1;;
6465                 for(var i = 0, ci; ci = c[i]; i++){
6466                     if(!prev(ci) && !next(ci)){
6467                         r[++ri] = ci;
6468                     }
6469                 }
6470                 return r;
6471             },
6472
6473             "empty" : function(c){
6474                 var r = [], ri = -1;
6475                 for(var i = 0, ci; ci = c[i]; i++){
6476                     var cns = ci.childNodes, j = 0, cn, empty = true;
6477                     while(cn = cns[j]){
6478                         ++j;
6479                         if(cn.nodeType == 1 || cn.nodeType == 3){
6480                             empty = false;
6481                             break;
6482                         }
6483                     }
6484                     if(empty){
6485                         r[++ri] = ci;
6486                     }
6487                 }
6488                 return r;
6489             },
6490
6491             "contains" : function(c, v){
6492                 var r = [], ri = -1;
6493                 for(var i = 0, ci; ci = c[i]; i++){
6494                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6495                         r[++ri] = ci;
6496                     }
6497                 }
6498                 return r;
6499             },
6500
6501             "nodeValue" : function(c, v){
6502                 var r = [], ri = -1;
6503                 for(var i = 0, ci; ci = c[i]; i++){
6504                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6505                         r[++ri] = ci;
6506                     }
6507                 }
6508                 return r;
6509             },
6510
6511             "checked" : function(c){
6512                 var r = [], ri = -1;
6513                 for(var i = 0, ci; ci = c[i]; i++){
6514                     if(ci.checked == true){
6515                         r[++ri] = ci;
6516                     }
6517                 }
6518                 return r;
6519             },
6520
6521             "not" : function(c, ss){
6522                 return Roo.DomQuery.filter(c, ss, true);
6523             },
6524
6525             "odd" : function(c){
6526                 return this["nth-child"](c, "odd");
6527             },
6528
6529             "even" : function(c){
6530                 return this["nth-child"](c, "even");
6531             },
6532
6533             "nth" : function(c, a){
6534                 return c[a-1] || [];
6535             },
6536
6537             "first" : function(c){
6538                 return c[0] || [];
6539             },
6540
6541             "last" : function(c){
6542                 return c[c.length-1] || [];
6543             },
6544
6545             "has" : function(c, ss){
6546                 var s = Roo.DomQuery.select;
6547                 var r = [], ri = -1;
6548                 for(var i = 0, ci; ci = c[i]; i++){
6549                     if(s(ss, ci).length > 0){
6550                         r[++ri] = ci;
6551                     }
6552                 }
6553                 return r;
6554             },
6555
6556             "next" : function(c, ss){
6557                 var is = Roo.DomQuery.is;
6558                 var r = [], ri = -1;
6559                 for(var i = 0, ci; ci = c[i]; i++){
6560                     var n = next(ci);
6561                     if(n && is(n, ss)){
6562                         r[++ri] = ci;
6563                     }
6564                 }
6565                 return r;
6566             },
6567
6568             "prev" : function(c, ss){
6569                 var is = Roo.DomQuery.is;
6570                 var r = [], ri = -1;
6571                 for(var i = 0, ci; ci = c[i]; i++){
6572                     var n = prev(ci);
6573                     if(n && is(n, ss)){
6574                         r[++ri] = ci;
6575                     }
6576                 }
6577                 return r;
6578             }
6579         }
6580     };
6581 }();
6582
6583 /**
6584  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6585  * @param {String} path The selector/xpath query
6586  * @param {Node} root (optional) The start of the query (defaults to document).
6587  * @return {Array}
6588  * @member Roo
6589  * @method query
6590  */
6591 Roo.query = Roo.DomQuery.select;
6592 /*
6593  * Based on:
6594  * Ext JS Library 1.1.1
6595  * Copyright(c) 2006-2007, Ext JS, LLC.
6596  *
6597  * Originally Released Under LGPL - original licence link has changed is not relivant.
6598  *
6599  * Fork - LGPL
6600  * <script type="text/javascript">
6601  */
6602
6603 /**
6604  * @class Roo.util.Observable
6605  * Base class that provides a common interface for publishing events. Subclasses are expected to
6606  * to have a property "events" with all the events defined.<br>
6607  * For example:
6608  * <pre><code>
6609  Employee = function(name){
6610     this.name = name;
6611     this.addEvents({
6612         "fired" : true,
6613         "quit" : true
6614     });
6615  }
6616  Roo.extend(Employee, Roo.util.Observable);
6617 </code></pre>
6618  * @param {Object} config properties to use (incuding events / listeners)
6619  */
6620
6621 Roo.util.Observable = function(cfg){
6622     
6623     cfg = cfg|| {};
6624     this.addEvents(cfg.events || {});
6625     if (cfg.events) {
6626         delete cfg.events; // make sure
6627     }
6628      
6629     Roo.apply(this, cfg);
6630     
6631     if(this.listeners){
6632         this.on(this.listeners);
6633         delete this.listeners;
6634     }
6635 };
6636 Roo.util.Observable.prototype = {
6637     /** 
6638  * @cfg {Object} listeners  list of events and functions to call for this object, 
6639  * For example :
6640  * <pre><code>
6641     listeners :  { 
6642        'click' : function(e) {
6643            ..... 
6644         } ,
6645         .... 
6646     } 
6647   </code></pre>
6648  */
6649     
6650     
6651     /**
6652      * Fires the specified event with the passed parameters (minus the event name).
6653      * @param {String} eventName
6654      * @param {Object...} args Variable number of parameters are passed to handlers
6655      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6656      */
6657     fireEvent : function(){
6658         var ce = this.events[arguments[0].toLowerCase()];
6659         if(typeof ce == "object"){
6660             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6661         }else{
6662             return true;
6663         }
6664     },
6665
6666     // private
6667     filterOptRe : /^(?:scope|delay|buffer|single)$/,
6668
6669     /**
6670      * Appends an event handler to this component
6671      * @param {String}   eventName The type of event to listen for
6672      * @param {Function} handler The method the event invokes
6673      * @param {Object}   scope (optional) The scope in which to execute the handler
6674      * function. The handler function's "this" context.
6675      * @param {Object}   options (optional) An object containing handler configuration
6676      * properties. This may contain any of the following properties:<ul>
6677      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6678      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6679      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6680      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6681      * by the specified number of milliseconds. If the event fires again within that time, the original
6682      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6683      * </ul><br>
6684      * <p>
6685      * <b>Combining Options</b><br>
6686      * Using the options argument, it is possible to combine different types of listeners:<br>
6687      * <br>
6688      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
6689                 <pre><code>
6690                 el.on('click', this.onClick, this, {
6691                         single: true,
6692                 delay: 100,
6693                 forumId: 4
6694                 });
6695                 </code></pre>
6696      * <p>
6697      * <b>Attaching multiple handlers in 1 call</b><br>
6698      * The method also allows for a single argument to be passed which is a config object containing properties
6699      * which specify multiple handlers.
6700      * <pre><code>
6701                 el.on({
6702                         'click': {
6703                         fn: this.onClick,
6704                         scope: this,
6705                         delay: 100
6706                 }, 
6707                 'mouseover': {
6708                         fn: this.onMouseOver,
6709                         scope: this
6710                 },
6711                 'mouseout': {
6712                         fn: this.onMouseOut,
6713                         scope: this
6714                 }
6715                 });
6716                 </code></pre>
6717      * <p>
6718      * Or a shorthand syntax which passes the same scope object to all handlers:
6719         <pre><code>
6720                 el.on({
6721                         'click': this.onClick,
6722                 'mouseover': this.onMouseOver,
6723                 'mouseout': this.onMouseOut,
6724                 scope: this
6725                 });
6726                 </code></pre>
6727      */
6728     addListener : function(eventName, fn, scope, o){
6729         if(typeof eventName == "object"){
6730             o = eventName;
6731             for(var e in o){
6732                 if(this.filterOptRe.test(e)){
6733                     continue;
6734                 }
6735                 if(typeof o[e] == "function"){
6736                     // shared options
6737                     this.addListener(e, o[e], o.scope,  o);
6738                 }else{
6739                     // individual options
6740                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
6741                 }
6742             }
6743             return;
6744         }
6745         o = (!o || typeof o == "boolean") ? {} : o;
6746         eventName = eventName.toLowerCase();
6747         var ce = this.events[eventName] || true;
6748         if(typeof ce == "boolean"){
6749             ce = new Roo.util.Event(this, eventName);
6750             this.events[eventName] = ce;
6751         }
6752         ce.addListener(fn, scope, o);
6753     },
6754
6755     /**
6756      * Removes a listener
6757      * @param {String}   eventName     The type of event to listen for
6758      * @param {Function} handler        The handler to remove
6759      * @param {Object}   scope  (optional) The scope (this object) for the handler
6760      */
6761     removeListener : function(eventName, fn, scope){
6762         var ce = this.events[eventName.toLowerCase()];
6763         if(typeof ce == "object"){
6764             ce.removeListener(fn, scope);
6765         }
6766     },
6767
6768     /**
6769      * Removes all listeners for this object
6770      */
6771     purgeListeners : function(){
6772         for(var evt in this.events){
6773             if(typeof this.events[evt] == "object"){
6774                  this.events[evt].clearListeners();
6775             }
6776         }
6777     },
6778
6779     relayEvents : function(o, events){
6780         var createHandler = function(ename){
6781             return function(){
6782                  
6783                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
6784             };
6785         };
6786         for(var i = 0, len = events.length; i < len; i++){
6787             var ename = events[i];
6788             if(!this.events[ename]){
6789                 this.events[ename] = true;
6790             };
6791             o.on(ename, createHandler(ename), this);
6792         }
6793     },
6794
6795     /**
6796      * Used to define events on this Observable
6797      * @param {Object} object The object with the events defined
6798      */
6799     addEvents : function(o){
6800         if(!this.events){
6801             this.events = {};
6802         }
6803         Roo.applyIf(this.events, o);
6804     },
6805
6806     /**
6807      * Checks to see if this object has any listeners for a specified event
6808      * @param {String} eventName The name of the event to check for
6809      * @return {Boolean} True if the event is being listened for, else false
6810      */
6811     hasListener : function(eventName){
6812         var e = this.events[eventName];
6813         return typeof e == "object" && e.listeners.length > 0;
6814     }
6815 };
6816 /**
6817  * Appends an event handler to this element (shorthand for addListener)
6818  * @param {String}   eventName     The type of event to listen for
6819  * @param {Function} handler        The method the event invokes
6820  * @param {Object}   scope (optional) The scope in which to execute the handler
6821  * function. The handler function's "this" context.
6822  * @param {Object}   options  (optional)
6823  * @method
6824  */
6825 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
6826 /**
6827  * Removes a listener (shorthand for removeListener)
6828  * @param {String}   eventName     The type of event to listen for
6829  * @param {Function} handler        The handler to remove
6830  * @param {Object}   scope  (optional) The scope (this object) for the handler
6831  * @method
6832  */
6833 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6834
6835 /**
6836  * Starts capture on the specified Observable. All events will be passed
6837  * to the supplied function with the event name + standard signature of the event
6838  * <b>before</b> the event is fired. If the supplied function returns false,
6839  * the event will not fire.
6840  * @param {Observable} o The Observable to capture
6841  * @param {Function} fn The function to call
6842  * @param {Object} scope (optional) The scope (this object) for the fn
6843  * @static
6844  */
6845 Roo.util.Observable.capture = function(o, fn, scope){
6846     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6847 };
6848
6849 /**
6850  * Removes <b>all</b> added captures from the Observable.
6851  * @param {Observable} o The Observable to release
6852  * @static
6853  */
6854 Roo.util.Observable.releaseCapture = function(o){
6855     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6856 };
6857
6858 (function(){
6859
6860     var createBuffered = function(h, o, scope){
6861         var task = new Roo.util.DelayedTask();
6862         return function(){
6863             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6864         };
6865     };
6866
6867     var createSingle = function(h, e, fn, scope){
6868         return function(){
6869             e.removeListener(fn, scope);
6870             return h.apply(scope, arguments);
6871         };
6872     };
6873
6874     var createDelayed = function(h, o, scope){
6875         return function(){
6876             var args = Array.prototype.slice.call(arguments, 0);
6877             setTimeout(function(){
6878                 h.apply(scope, args);
6879             }, o.delay || 10);
6880         };
6881     };
6882
6883     Roo.util.Event = function(obj, name){
6884         this.name = name;
6885         this.obj = obj;
6886         this.listeners = [];
6887     };
6888
6889     Roo.util.Event.prototype = {
6890         addListener : function(fn, scope, options){
6891             var o = options || {};
6892             scope = scope || this.obj;
6893             if(!this.isListening(fn, scope)){
6894                 var l = {fn: fn, scope: scope, options: o};
6895                 var h = fn;
6896                 if(o.delay){
6897                     h = createDelayed(h, o, scope);
6898                 }
6899                 if(o.single){
6900                     h = createSingle(h, this, fn, scope);
6901                 }
6902                 if(o.buffer){
6903                     h = createBuffered(h, o, scope);
6904                 }
6905                 l.fireFn = h;
6906                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6907                     this.listeners.push(l);
6908                 }else{
6909                     this.listeners = this.listeners.slice(0);
6910                     this.listeners.push(l);
6911                 }
6912             }
6913         },
6914
6915         findListener : function(fn, scope){
6916             scope = scope || this.obj;
6917             var ls = this.listeners;
6918             for(var i = 0, len = ls.length; i < len; i++){
6919                 var l = ls[i];
6920                 if(l.fn == fn && l.scope == scope){
6921                     return i;
6922                 }
6923             }
6924             return -1;
6925         },
6926
6927         isListening : function(fn, scope){
6928             return this.findListener(fn, scope) != -1;
6929         },
6930
6931         removeListener : function(fn, scope){
6932             var index;
6933             if((index = this.findListener(fn, scope)) != -1){
6934                 if(!this.firing){
6935                     this.listeners.splice(index, 1);
6936                 }else{
6937                     this.listeners = this.listeners.slice(0);
6938                     this.listeners.splice(index, 1);
6939                 }
6940                 return true;
6941             }
6942             return false;
6943         },
6944
6945         clearListeners : function(){
6946             this.listeners = [];
6947         },
6948
6949         fire : function(){
6950             var ls = this.listeners, scope, len = ls.length;
6951             if(len > 0){
6952                 this.firing = true;
6953                 var args = Array.prototype.slice.call(arguments, 0);                
6954                 for(var i = 0; i < len; i++){
6955                     var l = ls[i];
6956                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6957                         this.firing = false;
6958                         return false;
6959                     }
6960                 }
6961                 this.firing = false;
6962             }
6963             return true;
6964         }
6965     };
6966 })();/*
6967  * RooJS Library 
6968  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6969  *
6970  * Licence LGPL 
6971  *
6972  */
6973  
6974 /**
6975  * @class Roo.Document
6976  * @extends Roo.util.Observable
6977  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6978  * 
6979  * @param {Object} config the methods and properties of the 'base' class for the application.
6980  * 
6981  *  Generic Page handler - implement this to start your app..
6982  * 
6983  * eg.
6984  *  MyProject = new Roo.Document({
6985         events : {
6986             'load' : true // your events..
6987         },
6988         listeners : {
6989             'ready' : function() {
6990                 // fired on Roo.onReady()
6991             }
6992         }
6993  * 
6994  */
6995 Roo.Document = function(cfg) {
6996      
6997     this.addEvents({ 
6998         'ready' : true
6999     });
7000     Roo.util.Observable.call(this,cfg);
7001     
7002     var _this = this;
7003     
7004     Roo.onReady(function() {
7005         _this.fireEvent('ready');
7006     },null,false);
7007     
7008     
7009 }
7010
7011 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7012  * Based on:
7013  * Ext JS Library 1.1.1
7014  * Copyright(c) 2006-2007, Ext JS, LLC.
7015  *
7016  * Originally Released Under LGPL - original licence link has changed is not relivant.
7017  *
7018  * Fork - LGPL
7019  * <script type="text/javascript">
7020  */
7021
7022 /**
7023  * @class Roo.EventManager
7024  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7025  * several useful events directly.
7026  * See {@link Roo.EventObject} for more details on normalized event objects.
7027  * @static
7028  */
7029 Roo.EventManager = function(){
7030     var docReadyEvent, docReadyProcId, docReadyState = false;
7031     var resizeEvent, resizeTask, textEvent, textSize;
7032     var E = Roo.lib.Event;
7033     var D = Roo.lib.Dom;
7034
7035     
7036     
7037
7038     var fireDocReady = function(){
7039         if(!docReadyState){
7040             docReadyState = true;
7041             Roo.isReady = true;
7042             if(docReadyProcId){
7043                 clearInterval(docReadyProcId);
7044             }
7045             if(Roo.isGecko || Roo.isOpera) {
7046                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7047             }
7048             if(Roo.isIE){
7049                 var defer = document.getElementById("ie-deferred-loader");
7050                 if(defer){
7051                     defer.onreadystatechange = null;
7052                     defer.parentNode.removeChild(defer);
7053                 }
7054             }
7055             if(docReadyEvent){
7056                 docReadyEvent.fire();
7057                 docReadyEvent.clearListeners();
7058             }
7059         }
7060     };
7061     
7062     var initDocReady = function(){
7063         docReadyEvent = new Roo.util.Event();
7064         if(Roo.isGecko || Roo.isOpera) {
7065             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7066         }else if(Roo.isIE){
7067             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7068             var defer = document.getElementById("ie-deferred-loader");
7069             defer.onreadystatechange = function(){
7070                 if(this.readyState == "complete"){
7071                     fireDocReady();
7072                 }
7073             };
7074         }else if(Roo.isSafari){ 
7075             docReadyProcId = setInterval(function(){
7076                 var rs = document.readyState;
7077                 if(rs == "complete") {
7078                     fireDocReady();     
7079                  }
7080             }, 10);
7081         }
7082         // no matter what, make sure it fires on load
7083         E.on(window, "load", fireDocReady);
7084     };
7085
7086     var createBuffered = function(h, o){
7087         var task = new Roo.util.DelayedTask(h);
7088         return function(e){
7089             // create new event object impl so new events don't wipe out properties
7090             e = new Roo.EventObjectImpl(e);
7091             task.delay(o.buffer, h, null, [e]);
7092         };
7093     };
7094
7095     var createSingle = function(h, el, ename, fn){
7096         return function(e){
7097             Roo.EventManager.removeListener(el, ename, fn);
7098             h(e);
7099         };
7100     };
7101
7102     var createDelayed = function(h, o){
7103         return function(e){
7104             // create new event object impl so new events don't wipe out properties
7105             e = new Roo.EventObjectImpl(e);
7106             setTimeout(function(){
7107                 h(e);
7108             }, o.delay || 10);
7109         };
7110     };
7111     var transitionEndVal = false;
7112     
7113     var transitionEnd = function()
7114     {
7115         if (transitionEndVal) {
7116             return transitionEndVal;
7117         }
7118         var el = document.createElement('div');
7119
7120         var transEndEventNames = {
7121             WebkitTransition : 'webkitTransitionEnd',
7122             MozTransition    : 'transitionend',
7123             OTransition      : 'oTransitionEnd otransitionend',
7124             transition       : 'transitionend'
7125         };
7126     
7127         for (var name in transEndEventNames) {
7128             if (el.style[name] !== undefined) {
7129                 transitionEndVal = transEndEventNames[name];
7130                 return  transitionEndVal ;
7131             }
7132         }
7133     }
7134     
7135   
7136
7137     var listen = function(element, ename, opt, fn, scope)
7138     {
7139         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7140         fn = fn || o.fn; scope = scope || o.scope;
7141         var el = Roo.getDom(element);
7142         
7143         
7144         if(!el){
7145             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7146         }
7147         
7148         if (ename == 'transitionend') {
7149             ename = transitionEnd();
7150         }
7151         var h = function(e){
7152             e = Roo.EventObject.setEvent(e);
7153             var t;
7154             if(o.delegate){
7155                 t = e.getTarget(o.delegate, el);
7156                 if(!t){
7157                     return;
7158                 }
7159             }else{
7160                 t = e.target;
7161             }
7162             if(o.stopEvent === true){
7163                 e.stopEvent();
7164             }
7165             if(o.preventDefault === true){
7166                e.preventDefault();
7167             }
7168             if(o.stopPropagation === true){
7169                 e.stopPropagation();
7170             }
7171
7172             if(o.normalized === false){
7173                 e = e.browserEvent;
7174             }
7175
7176             fn.call(scope || el, e, t, o);
7177         };
7178         if(o.delay){
7179             h = createDelayed(h, o);
7180         }
7181         if(o.single){
7182             h = createSingle(h, el, ename, fn);
7183         }
7184         if(o.buffer){
7185             h = createBuffered(h, o);
7186         }
7187         
7188         fn._handlers = fn._handlers || [];
7189         
7190         
7191         fn._handlers.push([Roo.id(el), ename, h]);
7192         
7193         
7194          
7195         E.on(el, ename, h); // this adds the actuall listener to the object..
7196         
7197         
7198         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7199             el.addEventListener("DOMMouseScroll", h, false);
7200             E.on(window, 'unload', function(){
7201                 el.removeEventListener("DOMMouseScroll", h, false);
7202             });
7203         }
7204         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7205             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7206         }
7207         return h;
7208     };
7209
7210     var stopListening = function(el, ename, fn){
7211         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7212         if(hds){
7213             for(var i = 0, len = hds.length; i < len; i++){
7214                 var h = hds[i];
7215                 if(h[0] == id && h[1] == ename){
7216                     hd = h[2];
7217                     hds.splice(i, 1);
7218                     break;
7219                 }
7220             }
7221         }
7222         E.un(el, ename, hd);
7223         el = Roo.getDom(el);
7224         if(ename == "mousewheel" && el.addEventListener){
7225             el.removeEventListener("DOMMouseScroll", hd, false);
7226         }
7227         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7228             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7229         }
7230     };
7231
7232     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7233     
7234     var pub = {
7235         
7236         
7237         /** 
7238          * Fix for doc tools
7239          * @scope Roo.EventManager
7240          */
7241         
7242         
7243         /** 
7244          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7245          * object with a Roo.EventObject
7246          * @param {Function} fn        The method the event invokes
7247          * @param {Object}   scope    An object that becomes the scope of the handler
7248          * @param {boolean}  override If true, the obj passed in becomes
7249          *                             the execution scope of the listener
7250          * @return {Function} The wrapped function
7251          * @deprecated
7252          */
7253         wrap : function(fn, scope, override){
7254             return function(e){
7255                 Roo.EventObject.setEvent(e);
7256                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7257             };
7258         },
7259         
7260         /**
7261      * Appends an event handler to an element (shorthand for addListener)
7262      * @param {String/HTMLElement}   element        The html element or id to assign the
7263      * @param {String}   eventName The type of event to listen for
7264      * @param {Function} handler The method the event invokes
7265      * @param {Object}   scope (optional) The scope in which to execute the handler
7266      * function. The handler function's "this" context.
7267      * @param {Object}   options (optional) An object containing handler configuration
7268      * properties. This may contain any of the following properties:<ul>
7269      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7270      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7271      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7272      * <li>preventDefault {Boolean} True to prevent the default action</li>
7273      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7274      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7275      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7276      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7277      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7278      * by the specified number of milliseconds. If the event fires again within that time, the original
7279      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7280      * </ul><br>
7281      * <p>
7282      * <b>Combining Options</b><br>
7283      * Using the options argument, it is possible to combine different types of listeners:<br>
7284      * <br>
7285      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7286      * Code:<pre><code>
7287 el.on('click', this.onClick, this, {
7288     single: true,
7289     delay: 100,
7290     stopEvent : true,
7291     forumId: 4
7292 });</code></pre>
7293      * <p>
7294      * <b>Attaching multiple handlers in 1 call</b><br>
7295       * The method also allows for a single argument to be passed which is a config object containing properties
7296      * which specify multiple handlers.
7297      * <p>
7298      * Code:<pre><code>
7299 el.on({
7300     'click' : {
7301         fn: this.onClick
7302         scope: this,
7303         delay: 100
7304     },
7305     'mouseover' : {
7306         fn: this.onMouseOver
7307         scope: this
7308     },
7309     'mouseout' : {
7310         fn: this.onMouseOut
7311         scope: this
7312     }
7313 });</code></pre>
7314      * <p>
7315      * Or a shorthand syntax:<br>
7316      * Code:<pre><code>
7317 el.on({
7318     'click' : this.onClick,
7319     'mouseover' : this.onMouseOver,
7320     'mouseout' : this.onMouseOut
7321     scope: this
7322 });</code></pre>
7323      */
7324         addListener : function(element, eventName, fn, scope, options){
7325             if(typeof eventName == "object"){
7326                 var o = eventName;
7327                 for(var e in o){
7328                     if(propRe.test(e)){
7329                         continue;
7330                     }
7331                     if(typeof o[e] == "function"){
7332                         // shared options
7333                         listen(element, e, o, o[e], o.scope);
7334                     }else{
7335                         // individual options
7336                         listen(element, e, o[e]);
7337                     }
7338                 }
7339                 return;
7340             }
7341             return listen(element, eventName, options, fn, scope);
7342         },
7343         
7344         /**
7345          * Removes an event handler
7346          *
7347          * @param {String/HTMLElement}   element        The id or html element to remove the 
7348          *                             event from
7349          * @param {String}   eventName     The type of event
7350          * @param {Function} fn
7351          * @return {Boolean} True if a listener was actually removed
7352          */
7353         removeListener : function(element, eventName, fn){
7354             return stopListening(element, eventName, fn);
7355         },
7356         
7357         /**
7358          * Fires when the document is ready (before onload and before images are loaded). Can be 
7359          * accessed shorthanded Roo.onReady().
7360          * @param {Function} fn        The method the event invokes
7361          * @param {Object}   scope    An  object that becomes the scope of the handler
7362          * @param {boolean}  options
7363          */
7364         onDocumentReady : function(fn, scope, options){
7365             if(docReadyState){ // if it already fired
7366                 docReadyEvent.addListener(fn, scope, options);
7367                 docReadyEvent.fire();
7368                 docReadyEvent.clearListeners();
7369                 return;
7370             }
7371             if(!docReadyEvent){
7372                 initDocReady();
7373             }
7374             docReadyEvent.addListener(fn, scope, options);
7375         },
7376         
7377         /**
7378          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7379          * @param {Function} fn        The method the event invokes
7380          * @param {Object}   scope    An object that becomes the scope of the handler
7381          * @param {boolean}  options
7382          */
7383         onWindowResize : function(fn, scope, options)
7384         {
7385             if(!resizeEvent){
7386                 resizeEvent = new Roo.util.Event();
7387                 resizeTask = new Roo.util.DelayedTask(function(){
7388                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7389                 });
7390                 E.on(window, "resize", function()
7391                 {
7392                     if (Roo.isIE) {
7393                         resizeTask.delay(50);
7394                     } else {
7395                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7396                     }
7397                 });
7398             }
7399             resizeEvent.addListener(fn, scope, options);
7400         },
7401
7402         /**
7403          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7404          * @param {Function} fn        The method the event invokes
7405          * @param {Object}   scope    An object that becomes the scope of the handler
7406          * @param {boolean}  options
7407          */
7408         onTextResize : function(fn, scope, options){
7409             if(!textEvent){
7410                 textEvent = new Roo.util.Event();
7411                 var textEl = new Roo.Element(document.createElement('div'));
7412                 textEl.dom.className = 'x-text-resize';
7413                 textEl.dom.innerHTML = 'X';
7414                 textEl.appendTo(document.body);
7415                 textSize = textEl.dom.offsetHeight;
7416                 setInterval(function(){
7417                     if(textEl.dom.offsetHeight != textSize){
7418                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7419                     }
7420                 }, this.textResizeInterval);
7421             }
7422             textEvent.addListener(fn, scope, options);
7423         },
7424
7425         /**
7426          * Removes the passed window resize listener.
7427          * @param {Function} fn        The method the event invokes
7428          * @param {Object}   scope    The scope of handler
7429          */
7430         removeResizeListener : function(fn, scope){
7431             if(resizeEvent){
7432                 resizeEvent.removeListener(fn, scope);
7433             }
7434         },
7435
7436         // private
7437         fireResize : function(){
7438             if(resizeEvent){
7439                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7440             }   
7441         },
7442         /**
7443          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7444          */
7445         ieDeferSrc : false,
7446         /**
7447          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7448          */
7449         textResizeInterval : 50
7450     };
7451     
7452     /**
7453      * Fix for doc tools
7454      * @scopeAlias pub=Roo.EventManager
7455      */
7456     
7457      /**
7458      * Appends an event handler to an element (shorthand for addListener)
7459      * @param {String/HTMLElement}   element        The html element or id to assign the
7460      * @param {String}   eventName The type of event to listen for
7461      * @param {Function} handler The method the event invokes
7462      * @param {Object}   scope (optional) The scope in which to execute the handler
7463      * function. The handler function's "this" context.
7464      * @param {Object}   options (optional) An object containing handler configuration
7465      * properties. This may contain any of the following properties:<ul>
7466      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7467      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7468      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7469      * <li>preventDefault {Boolean} True to prevent the default action</li>
7470      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7471      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7472      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7473      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7474      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7475      * by the specified number of milliseconds. If the event fires again within that time, the original
7476      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7477      * </ul><br>
7478      * <p>
7479      * <b>Combining Options</b><br>
7480      * Using the options argument, it is possible to combine different types of listeners:<br>
7481      * <br>
7482      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7483      * Code:<pre><code>
7484 el.on('click', this.onClick, this, {
7485     single: true,
7486     delay: 100,
7487     stopEvent : true,
7488     forumId: 4
7489 });</code></pre>
7490      * <p>
7491      * <b>Attaching multiple handlers in 1 call</b><br>
7492       * The method also allows for a single argument to be passed which is a config object containing properties
7493      * which specify multiple handlers.
7494      * <p>
7495      * Code:<pre><code>
7496 el.on({
7497     'click' : {
7498         fn: this.onClick
7499         scope: this,
7500         delay: 100
7501     },
7502     'mouseover' : {
7503         fn: this.onMouseOver
7504         scope: this
7505     },
7506     'mouseout' : {
7507         fn: this.onMouseOut
7508         scope: this
7509     }
7510 });</code></pre>
7511      * <p>
7512      * Or a shorthand syntax:<br>
7513      * Code:<pre><code>
7514 el.on({
7515     'click' : this.onClick,
7516     'mouseover' : this.onMouseOver,
7517     'mouseout' : this.onMouseOut
7518     scope: this
7519 });</code></pre>
7520      */
7521     pub.on = pub.addListener;
7522     pub.un = pub.removeListener;
7523
7524     pub.stoppedMouseDownEvent = new Roo.util.Event();
7525     return pub;
7526 }();
7527 /**
7528   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
7529   * @param {Function} fn        The method the event invokes
7530   * @param {Object}   scope    An  object that becomes the scope of the handler
7531   * @param {boolean}  override If true, the obj passed in becomes
7532   *                             the execution scope of the listener
7533   * @member Roo
7534   * @method onReady
7535  */
7536 Roo.onReady = Roo.EventManager.onDocumentReady;
7537
7538 Roo.onReady(function(){
7539     var bd = Roo.get(document.body);
7540     if(!bd){ return; }
7541
7542     var cls = [
7543             Roo.isIE ? "roo-ie"
7544             : Roo.isIE11 ? "roo-ie11"
7545             : Roo.isEdge ? "roo-edge"
7546             : Roo.isGecko ? "roo-gecko"
7547             : Roo.isOpera ? "roo-opera"
7548             : Roo.isSafari ? "roo-safari" : ""];
7549
7550     if(Roo.isMac){
7551         cls.push("roo-mac");
7552     }
7553     if(Roo.isLinux){
7554         cls.push("roo-linux");
7555     }
7556     if(Roo.isIOS){
7557         cls.push("roo-ios");
7558     }
7559     if(Roo.isTouch){
7560         cls.push("roo-touch");
7561     }
7562     if(Roo.isBorderBox){
7563         cls.push('roo-border-box');
7564     }
7565     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7566         var p = bd.dom.parentNode;
7567         if(p){
7568             p.className += ' roo-strict';
7569         }
7570     }
7571     bd.addClass(cls.join(' '));
7572 });
7573
7574 /**
7575  * @class Roo.EventObject
7576  * EventObject exposes the Yahoo! UI Event functionality directly on the object
7577  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
7578  * Example:
7579  * <pre><code>
7580  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7581     e.preventDefault();
7582     var target = e.getTarget();
7583     ...
7584  }
7585  var myDiv = Roo.get("myDiv");
7586  myDiv.on("click", handleClick);
7587  //or
7588  Roo.EventManager.on("myDiv", 'click', handleClick);
7589  Roo.EventManager.addListener("myDiv", 'click', handleClick);
7590  </code></pre>
7591  * @static
7592  */
7593 Roo.EventObject = function(){
7594     
7595     var E = Roo.lib.Event;
7596     
7597     // safari keypress events for special keys return bad keycodes
7598     var safariKeys = {
7599         63234 : 37, // left
7600         63235 : 39, // right
7601         63232 : 38, // up
7602         63233 : 40, // down
7603         63276 : 33, // page up
7604         63277 : 34, // page down
7605         63272 : 46, // delete
7606         63273 : 36, // home
7607         63275 : 35  // end
7608     };
7609
7610     // normalize button clicks
7611     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7612                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7613
7614     Roo.EventObjectImpl = function(e){
7615         if(e){
7616             this.setEvent(e.browserEvent || e);
7617         }
7618     };
7619     Roo.EventObjectImpl.prototype = {
7620         /**
7621          * Used to fix doc tools.
7622          * @scope Roo.EventObject.prototype
7623          */
7624             
7625
7626         
7627         
7628         /** The normal browser event */
7629         browserEvent : null,
7630         /** The button pressed in a mouse event */
7631         button : -1,
7632         /** True if the shift key was down during the event */
7633         shiftKey : false,
7634         /** True if the control key was down during the event */
7635         ctrlKey : false,
7636         /** True if the alt key was down during the event */
7637         altKey : false,
7638
7639         /** Key constant 
7640         * @type Number */
7641         BACKSPACE : 8,
7642         /** Key constant 
7643         * @type Number */
7644         TAB : 9,
7645         /** Key constant 
7646         * @type Number */
7647         RETURN : 13,
7648         /** Key constant 
7649         * @type Number */
7650         ENTER : 13,
7651         /** Key constant 
7652         * @type Number */
7653         SHIFT : 16,
7654         /** Key constant 
7655         * @type Number */
7656         CONTROL : 17,
7657         /** Key constant 
7658         * @type Number */
7659         ESC : 27,
7660         /** Key constant 
7661         * @type Number */
7662         SPACE : 32,
7663         /** Key constant 
7664         * @type Number */
7665         PAGEUP : 33,
7666         /** Key constant 
7667         * @type Number */
7668         PAGEDOWN : 34,
7669         /** Key constant 
7670         * @type Number */
7671         END : 35,
7672         /** Key constant 
7673         * @type Number */
7674         HOME : 36,
7675         /** Key constant 
7676         * @type Number */
7677         LEFT : 37,
7678         /** Key constant 
7679         * @type Number */
7680         UP : 38,
7681         /** Key constant 
7682         * @type Number */
7683         RIGHT : 39,
7684         /** Key constant 
7685         * @type Number */
7686         DOWN : 40,
7687         /** Key constant 
7688         * @type Number */
7689         DELETE : 46,
7690         /** Key constant 
7691         * @type Number */
7692         F5 : 116,
7693
7694            /** @private */
7695         setEvent : function(e){
7696             if(e == this || (e && e.browserEvent)){ // already wrapped
7697                 return e;
7698             }
7699             this.browserEvent = e;
7700             if(e){
7701                 // normalize buttons
7702                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
7703                 if(e.type == 'click' && this.button == -1){
7704                     this.button = 0;
7705                 }
7706                 this.type = e.type;
7707                 this.shiftKey = e.shiftKey;
7708                 // mac metaKey behaves like ctrlKey
7709                 this.ctrlKey = e.ctrlKey || e.metaKey;
7710                 this.altKey = e.altKey;
7711                 // in getKey these will be normalized for the mac
7712                 this.keyCode = e.keyCode;
7713                 // keyup warnings on firefox.
7714                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
7715                 // cache the target for the delayed and or buffered events
7716                 this.target = E.getTarget(e);
7717                 // same for XY
7718                 this.xy = E.getXY(e);
7719             }else{
7720                 this.button = -1;
7721                 this.shiftKey = false;
7722                 this.ctrlKey = false;
7723                 this.altKey = false;
7724                 this.keyCode = 0;
7725                 this.charCode =0;
7726                 this.target = null;
7727                 this.xy = [0, 0];
7728             }
7729             return this;
7730         },
7731
7732         /**
7733          * Stop the event (preventDefault and stopPropagation)
7734          */
7735         stopEvent : function(){
7736             if(this.browserEvent){
7737                 if(this.browserEvent.type == 'mousedown'){
7738                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7739                 }
7740                 E.stopEvent(this.browserEvent);
7741             }
7742         },
7743
7744         /**
7745          * Prevents the browsers default handling of the event.
7746          */
7747         preventDefault : function(){
7748             if(this.browserEvent){
7749                 E.preventDefault(this.browserEvent);
7750             }
7751         },
7752
7753         /** @private */
7754         isNavKeyPress : function(){
7755             var k = this.keyCode;
7756             k = Roo.isSafari ? (safariKeys[k] || k) : k;
7757             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
7758         },
7759
7760         isSpecialKey : function(){
7761             var k = this.keyCode;
7762             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
7763             (k == 16) || (k == 17) ||
7764             (k >= 18 && k <= 20) ||
7765             (k >= 33 && k <= 35) ||
7766             (k >= 36 && k <= 39) ||
7767             (k >= 44 && k <= 45);
7768         },
7769         /**
7770          * Cancels bubbling of the event.
7771          */
7772         stopPropagation : function(){
7773             if(this.browserEvent){
7774                 if(this.type == 'mousedown'){
7775                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7776                 }
7777                 E.stopPropagation(this.browserEvent);
7778             }
7779         },
7780
7781         /**
7782          * Gets the key code for the event.
7783          * @return {Number}
7784          */
7785         getCharCode : function(){
7786             return this.charCode || this.keyCode;
7787         },
7788
7789         /**
7790          * Returns a normalized keyCode for the event.
7791          * @return {Number} The key code
7792          */
7793         getKey : function(){
7794             var k = this.keyCode || this.charCode;
7795             return Roo.isSafari ? (safariKeys[k] || k) : k;
7796         },
7797
7798         /**
7799          * Gets the x coordinate of the event.
7800          * @return {Number}
7801          */
7802         getPageX : function(){
7803             return this.xy[0];
7804         },
7805
7806         /**
7807          * Gets the y coordinate of the event.
7808          * @return {Number}
7809          */
7810         getPageY : function(){
7811             return this.xy[1];
7812         },
7813
7814         /**
7815          * Gets the time of the event.
7816          * @return {Number}
7817          */
7818         getTime : function(){
7819             if(this.browserEvent){
7820                 return E.getTime(this.browserEvent);
7821             }
7822             return null;
7823         },
7824
7825         /**
7826          * Gets the page coordinates of the event.
7827          * @return {Array} The xy values like [x, y]
7828          */
7829         getXY : function(){
7830             return this.xy;
7831         },
7832
7833         /**
7834          * Gets the target for the event.
7835          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7836          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7837                 search as a number or element (defaults to 10 || document.body)
7838          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7839          * @return {HTMLelement}
7840          */
7841         getTarget : function(selector, maxDepth, returnEl){
7842             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7843         },
7844         /**
7845          * Gets the related target.
7846          * @return {HTMLElement}
7847          */
7848         getRelatedTarget : function(){
7849             if(this.browserEvent){
7850                 return E.getRelatedTarget(this.browserEvent);
7851             }
7852             return null;
7853         },
7854
7855         /**
7856          * Normalizes mouse wheel delta across browsers
7857          * @return {Number} The delta
7858          */
7859         getWheelDelta : function(){
7860             var e = this.browserEvent;
7861             var delta = 0;
7862             if(e.wheelDelta){ /* IE/Opera. */
7863                 delta = e.wheelDelta/120;
7864             }else if(e.detail){ /* Mozilla case. */
7865                 delta = -e.detail/3;
7866             }
7867             return delta;
7868         },
7869
7870         /**
7871          * Returns true if the control, meta, shift or alt key was pressed during this event.
7872          * @return {Boolean}
7873          */
7874         hasModifier : function(){
7875             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7876         },
7877
7878         /**
7879          * Returns true if the target of this event equals el or is a child of el
7880          * @param {String/HTMLElement/Element} el
7881          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7882          * @return {Boolean}
7883          */
7884         within : function(el, related){
7885             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7886             return t && Roo.fly(el).contains(t);
7887         },
7888
7889         getPoint : function(){
7890             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7891         }
7892     };
7893
7894     return new Roo.EventObjectImpl();
7895 }();
7896             
7897     /*
7898  * Based on:
7899  * Ext JS Library 1.1.1
7900  * Copyright(c) 2006-2007, Ext JS, LLC.
7901  *
7902  * Originally Released Under LGPL - original licence link has changed is not relivant.
7903  *
7904  * Fork - LGPL
7905  * <script type="text/javascript">
7906  */
7907
7908  
7909 // was in Composite Element!??!?!
7910  
7911 (function(){
7912     var D = Roo.lib.Dom;
7913     var E = Roo.lib.Event;
7914     var A = Roo.lib.Anim;
7915
7916     // local style camelizing for speed
7917     var propCache = {};
7918     var camelRe = /(-[a-z])/gi;
7919     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7920     var view = document.defaultView;
7921
7922 /**
7923  * @class Roo.Element
7924  * Represents an Element in the DOM.<br><br>
7925  * Usage:<br>
7926 <pre><code>
7927 var el = Roo.get("my-div");
7928
7929 // or with getEl
7930 var el = getEl("my-div");
7931
7932 // or with a DOM element
7933 var el = Roo.get(myDivElement);
7934 </code></pre>
7935  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7936  * each call instead of constructing a new one.<br><br>
7937  * <b>Animations</b><br />
7938  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7939  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7940 <pre>
7941 Option    Default   Description
7942 --------- --------  ---------------------------------------------
7943 duration  .35       The duration of the animation in seconds
7944 easing    easeOut   The YUI easing method
7945 callback  none      A function to execute when the anim completes
7946 scope     this      The scope (this) of the callback function
7947 </pre>
7948 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7949 * manipulate the animation. Here's an example:
7950 <pre><code>
7951 var el = Roo.get("my-div");
7952
7953 // no animation
7954 el.setWidth(100);
7955
7956 // default animation
7957 el.setWidth(100, true);
7958
7959 // animation with some options set
7960 el.setWidth(100, {
7961     duration: 1,
7962     callback: this.foo,
7963     scope: this
7964 });
7965
7966 // using the "anim" property to get the Anim object
7967 var opt = {
7968     duration: 1,
7969     callback: this.foo,
7970     scope: this
7971 };
7972 el.setWidth(100, opt);
7973 ...
7974 if(opt.anim.isAnimated()){
7975     opt.anim.stop();
7976 }
7977 </code></pre>
7978 * <b> Composite (Collections of) Elements</b><br />
7979  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7980  * @constructor Create a new Element directly.
7981  * @param {String/HTMLElement} element
7982  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
7983  */
7984     Roo.Element = function(element, forceNew)
7985     {
7986         var dom = typeof element == "string" ?
7987                 document.getElementById(element) : element;
7988         
7989         this.listeners = {};
7990         
7991         if(!dom){ // invalid id/element
7992             return null;
7993         }
7994         var id = dom.id;
7995         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7996             return Roo.Element.cache[id];
7997         }
7998
7999         /**
8000          * The DOM element
8001          * @type HTMLElement
8002          */
8003         this.dom = dom;
8004
8005         /**
8006          * The DOM element ID
8007          * @type String
8008          */
8009         this.id = id || Roo.id(dom);
8010         
8011         return this; // assumed for cctor?
8012     };
8013
8014     var El = Roo.Element;
8015
8016     El.prototype = {
8017         /**
8018          * The element's default display mode  (defaults to "") 
8019          * @type String
8020          */
8021         originalDisplay : "",
8022
8023         
8024         // note this is overridden in BS version..
8025         visibilityMode : 1, 
8026         /**
8027          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8028          * @type String
8029          */
8030         defaultUnit : "px",
8031         
8032         /**
8033          * Sets the element's visibility mode. When setVisible() is called it
8034          * will use this to determine whether to set the visibility or the display property.
8035          * @param visMode Element.VISIBILITY or Element.DISPLAY
8036          * @return {Roo.Element} this
8037          */
8038         setVisibilityMode : function(visMode){
8039             this.visibilityMode = visMode;
8040             return this;
8041         },
8042         /**
8043          * Convenience method for setVisibilityMode(Element.DISPLAY)
8044          * @param {String} display (optional) What to set display to when visible
8045          * @return {Roo.Element} this
8046          */
8047         enableDisplayMode : function(display){
8048             this.setVisibilityMode(El.DISPLAY);
8049             if(typeof display != "undefined") { this.originalDisplay = display; }
8050             return this;
8051         },
8052
8053         /**
8054          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8055          * @param {String} selector The simple selector to test
8056          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8057                 search as a number or element (defaults to 10 || document.body)
8058          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8059          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8060          */
8061         findParent : function(simpleSelector, maxDepth, returnEl){
8062             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8063             maxDepth = maxDepth || 50;
8064             if(typeof maxDepth != "number"){
8065                 stopEl = Roo.getDom(maxDepth);
8066                 maxDepth = 10;
8067             }
8068             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8069                 if(dq.is(p, simpleSelector)){
8070                     return returnEl ? Roo.get(p) : p;
8071                 }
8072                 depth++;
8073                 p = p.parentNode;
8074             }
8075             return null;
8076         },
8077
8078
8079         /**
8080          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8081          * @param {String} selector The simple selector to test
8082          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8083                 search as a number or element (defaults to 10 || document.body)
8084          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8085          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8086          */
8087         findParentNode : function(simpleSelector, maxDepth, returnEl){
8088             var p = Roo.fly(this.dom.parentNode, '_internal');
8089             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8090         },
8091         
8092         /**
8093          * Looks at  the scrollable parent element
8094          */
8095         findScrollableParent : function()
8096         {
8097             var overflowRegex = /(auto|scroll)/;
8098             
8099             if(this.getStyle('position') === 'fixed'){
8100                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8101             }
8102             
8103             var excludeStaticParent = this.getStyle('position') === "absolute";
8104             
8105             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8106                 
8107                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8108                     continue;
8109                 }
8110                 
8111                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8112                     return parent;
8113                 }
8114                 
8115                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8116                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8117                 }
8118             }
8119             
8120             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8121         },
8122
8123         /**
8124          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8125          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8126          * @param {String} selector The simple selector to test
8127          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8128                 search as a number or element (defaults to 10 || document.body)
8129          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8130          */
8131         up : function(simpleSelector, maxDepth){
8132             return this.findParentNode(simpleSelector, maxDepth, true);
8133         },
8134
8135
8136
8137         /**
8138          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8139          * @param {String} selector The simple selector to test
8140          * @return {Boolean} True if this element matches the selector, else false
8141          */
8142         is : function(simpleSelector){
8143             return Roo.DomQuery.is(this.dom, simpleSelector);
8144         },
8145
8146         /**
8147          * Perform animation on this element.
8148          * @param {Object} args The YUI animation control args
8149          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8150          * @param {Function} onComplete (optional) Function to call when animation completes
8151          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8152          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8153          * @return {Roo.Element} this
8154          */
8155         animate : function(args, duration, onComplete, easing, animType){
8156             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8157             return this;
8158         },
8159
8160         /*
8161          * @private Internal animation call
8162          */
8163         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8164             animType = animType || 'run';
8165             opt = opt || {};
8166             var anim = Roo.lib.Anim[animType](
8167                 this.dom, args,
8168                 (opt.duration || defaultDur) || .35,
8169                 (opt.easing || defaultEase) || 'easeOut',
8170                 function(){
8171                     Roo.callback(cb, this);
8172                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8173                 },
8174                 this
8175             );
8176             opt.anim = anim;
8177             return anim;
8178         },
8179
8180         // private legacy anim prep
8181         preanim : function(a, i){
8182             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8183         },
8184
8185         /**
8186          * Removes worthless text nodes
8187          * @param {Boolean} forceReclean (optional) By default the element
8188          * keeps track if it has been cleaned already so
8189          * you can call this over and over. However, if you update the element and
8190          * need to force a reclean, you can pass true.
8191          */
8192         clean : function(forceReclean){
8193             if(this.isCleaned && forceReclean !== true){
8194                 return this;
8195             }
8196             var ns = /\S/;
8197             var d = this.dom, n = d.firstChild, ni = -1;
8198             while(n){
8199                 var nx = n.nextSibling;
8200                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8201                     d.removeChild(n);
8202                 }else{
8203                     n.nodeIndex = ++ni;
8204                 }
8205                 n = nx;
8206             }
8207             this.isCleaned = true;
8208             return this;
8209         },
8210
8211         // private
8212         calcOffsetsTo : function(el){
8213             el = Roo.get(el);
8214             var d = el.dom;
8215             var restorePos = false;
8216             if(el.getStyle('position') == 'static'){
8217                 el.position('relative');
8218                 restorePos = true;
8219             }
8220             var x = 0, y =0;
8221             var op = this.dom;
8222             while(op && op != d && op.tagName != 'HTML'){
8223                 x+= op.offsetLeft;
8224                 y+= op.offsetTop;
8225                 op = op.offsetParent;
8226             }
8227             if(restorePos){
8228                 el.position('static');
8229             }
8230             return [x, y];
8231         },
8232
8233         /**
8234          * Scrolls this element into view within the passed container.
8235          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8236          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8237          * @return {Roo.Element} this
8238          */
8239         scrollIntoView : function(container, hscroll){
8240             var c = Roo.getDom(container) || document.body;
8241             var el = this.dom;
8242
8243             var o = this.calcOffsetsTo(c),
8244                 l = o[0],
8245                 t = o[1],
8246                 b = t+el.offsetHeight,
8247                 r = l+el.offsetWidth;
8248
8249             var ch = c.clientHeight;
8250             var ct = parseInt(c.scrollTop, 10);
8251             var cl = parseInt(c.scrollLeft, 10);
8252             var cb = ct + ch;
8253             var cr = cl + c.clientWidth;
8254
8255             if(t < ct){
8256                 c.scrollTop = t;
8257             }else if(b > cb){
8258                 c.scrollTop = b-ch;
8259             }
8260
8261             if(hscroll !== false){
8262                 if(l < cl){
8263                     c.scrollLeft = l;
8264                 }else if(r > cr){
8265                     c.scrollLeft = r-c.clientWidth;
8266                 }
8267             }
8268             return this;
8269         },
8270
8271         // private
8272         scrollChildIntoView : function(child, hscroll){
8273             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8274         },
8275
8276         /**
8277          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8278          * the new height may not be available immediately.
8279          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8280          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8281          * @param {Function} onComplete (optional) Function to call when animation completes
8282          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8283          * @return {Roo.Element} this
8284          */
8285         autoHeight : function(animate, duration, onComplete, easing){
8286             var oldHeight = this.getHeight();
8287             this.clip();
8288             this.setHeight(1); // force clipping
8289             setTimeout(function(){
8290                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8291                 if(!animate){
8292                     this.setHeight(height);
8293                     this.unclip();
8294                     if(typeof onComplete == "function"){
8295                         onComplete();
8296                     }
8297                 }else{
8298                     this.setHeight(oldHeight); // restore original height
8299                     this.setHeight(height, animate, duration, function(){
8300                         this.unclip();
8301                         if(typeof onComplete == "function") { onComplete(); }
8302                     }.createDelegate(this), easing);
8303                 }
8304             }.createDelegate(this), 0);
8305             return this;
8306         },
8307
8308         /**
8309          * Returns true if this element is an ancestor of the passed element
8310          * @param {HTMLElement/String} el The element to check
8311          * @return {Boolean} True if this element is an ancestor of el, else false
8312          */
8313         contains : function(el){
8314             if(!el){return false;}
8315             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8316         },
8317
8318         /**
8319          * Checks whether the element is currently visible using both visibility and display properties.
8320          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8321          * @return {Boolean} True if the element is currently visible, else false
8322          */
8323         isVisible : function(deep) {
8324             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8325             if(deep !== true || !vis){
8326                 return vis;
8327             }
8328             var p = this.dom.parentNode;
8329             while(p && p.tagName.toLowerCase() != "body"){
8330                 if(!Roo.fly(p, '_isVisible').isVisible()){
8331                     return false;
8332                 }
8333                 p = p.parentNode;
8334             }
8335             return true;
8336         },
8337
8338         /**
8339          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8340          * @param {String} selector The CSS selector
8341          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8342          * @return {CompositeElement/CompositeElementLite} The composite element
8343          */
8344         select : function(selector, unique){
8345             return El.select(selector, unique, this.dom);
8346         },
8347
8348         /**
8349          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8350          * @param {String} selector The CSS selector
8351          * @return {Array} An array of the matched nodes
8352          */
8353         query : function(selector, unique){
8354             return Roo.DomQuery.select(selector, this.dom);
8355         },
8356
8357         /**
8358          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8359          * @param {String} selector The CSS selector
8360          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8361          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8362          */
8363         child : function(selector, returnDom){
8364             var n = Roo.DomQuery.selectNode(selector, this.dom);
8365             return returnDom ? n : Roo.get(n);
8366         },
8367
8368         /**
8369          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8370          * @param {String} selector The CSS selector
8371          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8372          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8373          */
8374         down : function(selector, returnDom){
8375             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8376             return returnDom ? n : Roo.get(n);
8377         },
8378
8379         /**
8380          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8381          * @param {String} group The group the DD object is member of
8382          * @param {Object} config The DD config object
8383          * @param {Object} overrides An object containing methods to override/implement on the DD object
8384          * @return {Roo.dd.DD} The DD object
8385          */
8386         initDD : function(group, config, overrides){
8387             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8388             return Roo.apply(dd, overrides);
8389         },
8390
8391         /**
8392          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8393          * @param {String} group The group the DDProxy object is member of
8394          * @param {Object} config The DDProxy config object
8395          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8396          * @return {Roo.dd.DDProxy} The DDProxy object
8397          */
8398         initDDProxy : function(group, config, overrides){
8399             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8400             return Roo.apply(dd, overrides);
8401         },
8402
8403         /**
8404          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8405          * @param {String} group The group the DDTarget object is member of
8406          * @param {Object} config The DDTarget config object
8407          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8408          * @return {Roo.dd.DDTarget} The DDTarget object
8409          */
8410         initDDTarget : function(group, config, overrides){
8411             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8412             return Roo.apply(dd, overrides);
8413         },
8414
8415         /**
8416          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8417          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8418          * @param {Boolean} visible Whether the element is visible
8419          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8420          * @return {Roo.Element} this
8421          */
8422          setVisible : function(visible, animate){
8423             if(!animate || !A){
8424                 if(this.visibilityMode == El.DISPLAY){
8425                     this.setDisplayed(visible);
8426                 }else{
8427                     this.fixDisplay();
8428                     this.dom.style.visibility = visible ? "visible" : "hidden";
8429                 }
8430             }else{
8431                 // closure for composites
8432                 var dom = this.dom;
8433                 var visMode = this.visibilityMode;
8434                 if(visible){
8435                     this.setOpacity(.01);
8436                     this.setVisible(true);
8437                 }
8438                 this.anim({opacity: { to: (visible?1:0) }},
8439                       this.preanim(arguments, 1),
8440                       null, .35, 'easeIn', function(){
8441                          if(!visible){
8442                              if(visMode == El.DISPLAY){
8443                                  dom.style.display = "none";
8444                              }else{
8445                                  dom.style.visibility = "hidden";
8446                              }
8447                              Roo.get(dom).setOpacity(1);
8448                          }
8449                      });
8450             }
8451             return this;
8452         },
8453
8454         /**
8455          * Returns true if display is not "none"
8456          * @return {Boolean}
8457          */
8458         isDisplayed : function() {
8459             return this.getStyle("display") != "none";
8460         },
8461
8462         /**
8463          * Toggles the element's visibility or display, depending on visibility mode.
8464          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8465          * @return {Roo.Element} this
8466          */
8467         toggle : function(animate){
8468             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8469             return this;
8470         },
8471
8472         /**
8473          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8474          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8475          * @return {Roo.Element} this
8476          */
8477         setDisplayed : function(value) {
8478             if(typeof value == "boolean"){
8479                value = value ? this.originalDisplay : "none";
8480             }
8481             this.setStyle("display", value);
8482             return this;
8483         },
8484
8485         /**
8486          * Tries to focus the element. Any exceptions are caught and ignored.
8487          * @return {Roo.Element} this
8488          */
8489         focus : function() {
8490             try{
8491                 this.dom.focus();
8492             }catch(e){}
8493             return this;
8494         },
8495
8496         /**
8497          * Tries to blur the element. Any exceptions are caught and ignored.
8498          * @return {Roo.Element} this
8499          */
8500         blur : function() {
8501             try{
8502                 this.dom.blur();
8503             }catch(e){}
8504             return this;
8505         },
8506
8507         /**
8508          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8509          * @param {String/Array} className The CSS class to add, or an array of classes
8510          * @return {Roo.Element} this
8511          */
8512         addClass : function(className){
8513             if(className instanceof Array){
8514                 for(var i = 0, len = className.length; i < len; i++) {
8515                     this.addClass(className[i]);
8516                 }
8517             }else{
8518                 if(className && !this.hasClass(className)){
8519                     if (this.dom instanceof SVGElement) {
8520                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
8521                     } else {
8522                         this.dom.className = this.dom.className + " " + className;
8523                     }
8524                 }
8525             }
8526             return this;
8527         },
8528
8529         /**
8530          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8531          * @param {String/Array} className The CSS class to add, or an array of classes
8532          * @return {Roo.Element} this
8533          */
8534         radioClass : function(className){
8535             var siblings = this.dom.parentNode.childNodes;
8536             for(var i = 0; i < siblings.length; i++) {
8537                 var s = siblings[i];
8538                 if(s.nodeType == 1){
8539                     Roo.get(s).removeClass(className);
8540                 }
8541             }
8542             this.addClass(className);
8543             return this;
8544         },
8545
8546         /**
8547          * Removes one or more CSS classes from the element.
8548          * @param {String/Array} className The CSS class to remove, or an array of classes
8549          * @return {Roo.Element} this
8550          */
8551         removeClass : function(className){
8552             
8553             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8554             if(!className || !cn){
8555                 return this;
8556             }
8557             if(className instanceof Array){
8558                 for(var i = 0, len = className.length; i < len; i++) {
8559                     this.removeClass(className[i]);
8560                 }
8561             }else{
8562                 if(this.hasClass(className)){
8563                     var re = this.classReCache[className];
8564                     if (!re) {
8565                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8566                        this.classReCache[className] = re;
8567                     }
8568                     if (this.dom instanceof SVGElement) {
8569                         this.dom.className.baseVal = cn.replace(re, " ");
8570                     } else {
8571                         this.dom.className = cn.replace(re, " ");
8572                     }
8573                 }
8574             }
8575             return this;
8576         },
8577
8578         // private
8579         classReCache: {},
8580
8581         /**
8582          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8583          * @param {String} className The CSS class to toggle
8584          * @return {Roo.Element} this
8585          */
8586         toggleClass : function(className){
8587             if(this.hasClass(className)){
8588                 this.removeClass(className);
8589             }else{
8590                 this.addClass(className);
8591             }
8592             return this;
8593         },
8594
8595         /**
8596          * Checks if the specified CSS class exists on this element's DOM node.
8597          * @param {String} className The CSS class to check for
8598          * @return {Boolean} True if the class exists, else false
8599          */
8600         hasClass : function(className){
8601             if (this.dom instanceof SVGElement) {
8602                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
8603             } 
8604             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8605         },
8606
8607         /**
8608          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
8609          * @param {String} oldClassName The CSS class to replace
8610          * @param {String} newClassName The replacement CSS class
8611          * @return {Roo.Element} this
8612          */
8613         replaceClass : function(oldClassName, newClassName){
8614             this.removeClass(oldClassName);
8615             this.addClass(newClassName);
8616             return this;
8617         },
8618
8619         /**
8620          * Returns an object with properties matching the styles requested.
8621          * For example, el.getStyles('color', 'font-size', 'width') might return
8622          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8623          * @param {String} style1 A style name
8624          * @param {String} style2 A style name
8625          * @param {String} etc.
8626          * @return {Object} The style object
8627          */
8628         getStyles : function(){
8629             var a = arguments, len = a.length, r = {};
8630             for(var i = 0; i < len; i++){
8631                 r[a[i]] = this.getStyle(a[i]);
8632             }
8633             return r;
8634         },
8635
8636         /**
8637          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8638          * @param {String} property The style property whose value is returned.
8639          * @return {String} The current value of the style property for this element.
8640          */
8641         getStyle : function(){
8642             return view && view.getComputedStyle ?
8643                 function(prop){
8644                     var el = this.dom, v, cs, camel;
8645                     if(prop == 'float'){
8646                         prop = "cssFloat";
8647                     }
8648                     if(el.style && (v = el.style[prop])){
8649                         return v;
8650                     }
8651                     if(cs = view.getComputedStyle(el, "")){
8652                         if(!(camel = propCache[prop])){
8653                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
8654                         }
8655                         return cs[camel];
8656                     }
8657                     return null;
8658                 } :
8659                 function(prop){
8660                     var el = this.dom, v, cs, camel;
8661                     if(prop == 'opacity'){
8662                         if(typeof el.style.filter == 'string'){
8663                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8664                             if(m){
8665                                 var fv = parseFloat(m[1]);
8666                                 if(!isNaN(fv)){
8667                                     return fv ? fv / 100 : 0;
8668                                 }
8669                             }
8670                         }
8671                         return 1;
8672                     }else if(prop == 'float'){
8673                         prop = "styleFloat";
8674                     }
8675                     if(!(camel = propCache[prop])){
8676                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
8677                     }
8678                     if(v = el.style[camel]){
8679                         return v;
8680                     }
8681                     if(cs = el.currentStyle){
8682                         return cs[camel];
8683                     }
8684                     return null;
8685                 };
8686         }(),
8687
8688         /**
8689          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
8690          * @param {String/Object} property The style property to be set, or an object of multiple styles.
8691          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
8692          * @return {Roo.Element} this
8693          */
8694         setStyle : function(prop, value){
8695             if(typeof prop == "string"){
8696                 
8697                 if (prop == 'float') {
8698                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
8699                     return this;
8700                 }
8701                 
8702                 var camel;
8703                 if(!(camel = propCache[prop])){
8704                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
8705                 }
8706                 
8707                 if(camel == 'opacity') {
8708                     this.setOpacity(value);
8709                 }else{
8710                     this.dom.style[camel] = value;
8711                 }
8712             }else{
8713                 for(var style in prop){
8714                     if(typeof prop[style] != "function"){
8715                        this.setStyle(style, prop[style]);
8716                     }
8717                 }
8718             }
8719             return this;
8720         },
8721
8722         /**
8723          * More flexible version of {@link #setStyle} for setting style properties.
8724          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
8725          * a function which returns such a specification.
8726          * @return {Roo.Element} this
8727          */
8728         applyStyles : function(style){
8729             Roo.DomHelper.applyStyles(this.dom, style);
8730             return this;
8731         },
8732
8733         /**
8734           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8735           * @return {Number} The X position of the element
8736           */
8737         getX : function(){
8738             return D.getX(this.dom);
8739         },
8740
8741         /**
8742           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8743           * @return {Number} The Y position of the element
8744           */
8745         getY : function(){
8746             return D.getY(this.dom);
8747         },
8748
8749         /**
8750           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8751           * @return {Array} The XY position of the element
8752           */
8753         getXY : function(){
8754             return D.getXY(this.dom);
8755         },
8756
8757         /**
8758          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8759          * @param {Number} The X position of the element
8760          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8761          * @return {Roo.Element} this
8762          */
8763         setX : function(x, animate){
8764             if(!animate || !A){
8765                 D.setX(this.dom, x);
8766             }else{
8767                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
8768             }
8769             return this;
8770         },
8771
8772         /**
8773          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8774          * @param {Number} The Y position of the element
8775          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8776          * @return {Roo.Element} this
8777          */
8778         setY : function(y, animate){
8779             if(!animate || !A){
8780                 D.setY(this.dom, y);
8781             }else{
8782                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
8783             }
8784             return this;
8785         },
8786
8787         /**
8788          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
8789          * @param {String} left The left CSS property value
8790          * @return {Roo.Element} this
8791          */
8792         setLeft : function(left){
8793             this.setStyle("left", this.addUnits(left));
8794             return this;
8795         },
8796
8797         /**
8798          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
8799          * @param {String} top The top CSS property value
8800          * @return {Roo.Element} this
8801          */
8802         setTop : function(top){
8803             this.setStyle("top", this.addUnits(top));
8804             return this;
8805         },
8806
8807         /**
8808          * Sets the element's CSS right style.
8809          * @param {String} right The right CSS property value
8810          * @return {Roo.Element} this
8811          */
8812         setRight : function(right){
8813             this.setStyle("right", this.addUnits(right));
8814             return this;
8815         },
8816
8817         /**
8818          * Sets the element's CSS bottom style.
8819          * @param {String} bottom The bottom CSS property value
8820          * @return {Roo.Element} this
8821          */
8822         setBottom : function(bottom){
8823             this.setStyle("bottom", this.addUnits(bottom));
8824             return this;
8825         },
8826
8827         /**
8828          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8829          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8830          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8831          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8832          * @return {Roo.Element} this
8833          */
8834         setXY : function(pos, animate){
8835             if(!animate || !A){
8836                 D.setXY(this.dom, pos);
8837             }else{
8838                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
8839             }
8840             return this;
8841         },
8842
8843         /**
8844          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8845          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8846          * @param {Number} x X value for new position (coordinates are page-based)
8847          * @param {Number} y Y value for new position (coordinates are page-based)
8848          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8849          * @return {Roo.Element} this
8850          */
8851         setLocation : function(x, y, animate){
8852             this.setXY([x, y], this.preanim(arguments, 2));
8853             return this;
8854         },
8855
8856         /**
8857          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8858          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8859          * @param {Number} x X value for new position (coordinates are page-based)
8860          * @param {Number} y Y value for new position (coordinates are page-based)
8861          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8862          * @return {Roo.Element} this
8863          */
8864         moveTo : function(x, y, animate){
8865             this.setXY([x, y], this.preanim(arguments, 2));
8866             return this;
8867         },
8868
8869         /**
8870          * Returns the region of the given element.
8871          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8872          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8873          */
8874         getRegion : function(){
8875             return D.getRegion(this.dom);
8876         },
8877
8878         /**
8879          * Returns the offset height of the element
8880          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8881          * @return {Number} The element's height
8882          */
8883         getHeight : function(contentHeight){
8884             var h = this.dom.offsetHeight || 0;
8885             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8886         },
8887
8888         /**
8889          * Returns the offset width of the element
8890          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8891          * @return {Number} The element's width
8892          */
8893         getWidth : function(contentWidth){
8894             var w = this.dom.offsetWidth || 0;
8895             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8896         },
8897
8898         /**
8899          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8900          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8901          * if a height has not been set using CSS.
8902          * @return {Number}
8903          */
8904         getComputedHeight : function(){
8905             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8906             if(!h){
8907                 h = parseInt(this.getStyle('height'), 10) || 0;
8908                 if(!this.isBorderBox()){
8909                     h += this.getFrameWidth('tb');
8910                 }
8911             }
8912             return h;
8913         },
8914
8915         /**
8916          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8917          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8918          * if a width has not been set using CSS.
8919          * @return {Number}
8920          */
8921         getComputedWidth : function(){
8922             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8923             if(!w){
8924                 w = parseInt(this.getStyle('width'), 10) || 0;
8925                 if(!this.isBorderBox()){
8926                     w += this.getFrameWidth('lr');
8927                 }
8928             }
8929             return w;
8930         },
8931
8932         /**
8933          * Returns the size of the element.
8934          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8935          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8936          */
8937         getSize : function(contentSize){
8938             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8939         },
8940
8941         /**
8942          * Returns the width and height of the viewport.
8943          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8944          */
8945         getViewSize : function(){
8946             var d = this.dom, doc = document, aw = 0, ah = 0;
8947             if(d == doc || d == doc.body){
8948                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8949             }else{
8950                 return {
8951                     width : d.clientWidth,
8952                     height: d.clientHeight
8953                 };
8954             }
8955         },
8956
8957         /**
8958          * Returns the value of the "value" attribute
8959          * @param {Boolean} asNumber true to parse the value as a number
8960          * @return {String/Number}
8961          */
8962         getValue : function(asNumber){
8963             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8964         },
8965
8966         // private
8967         adjustWidth : function(width){
8968             if(typeof width == "number"){
8969                 if(this.autoBoxAdjust && !this.isBorderBox()){
8970                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8971                 }
8972                 if(width < 0){
8973                     width = 0;
8974                 }
8975             }
8976             return width;
8977         },
8978
8979         // private
8980         adjustHeight : function(height){
8981             if(typeof height == "number"){
8982                if(this.autoBoxAdjust && !this.isBorderBox()){
8983                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8984                }
8985                if(height < 0){
8986                    height = 0;
8987                }
8988             }
8989             return height;
8990         },
8991
8992         /**
8993          * Set the width of the element
8994          * @param {Number} width The new width
8995          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8996          * @return {Roo.Element} this
8997          */
8998         setWidth : function(width, animate){
8999             width = this.adjustWidth(width);
9000             if(!animate || !A){
9001                 this.dom.style.width = this.addUnits(width);
9002             }else{
9003                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9004             }
9005             return this;
9006         },
9007
9008         /**
9009          * Set the height of the element
9010          * @param {Number} height The new height
9011          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9012          * @return {Roo.Element} this
9013          */
9014          setHeight : function(height, animate){
9015             height = this.adjustHeight(height);
9016             if(!animate || !A){
9017                 this.dom.style.height = this.addUnits(height);
9018             }else{
9019                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9020             }
9021             return this;
9022         },
9023
9024         /**
9025          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9026          * @param {Number} width The new width
9027          * @param {Number} height The new height
9028          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9029          * @return {Roo.Element} this
9030          */
9031          setSize : function(width, height, animate){
9032             if(typeof width == "object"){ // in case of object from getSize()
9033                 height = width.height; width = width.width;
9034             }
9035             width = this.adjustWidth(width); height = this.adjustHeight(height);
9036             if(!animate || !A){
9037                 this.dom.style.width = this.addUnits(width);
9038                 this.dom.style.height = this.addUnits(height);
9039             }else{
9040                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9041             }
9042             return this;
9043         },
9044
9045         /**
9046          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9047          * @param {Number} x X value for new position (coordinates are page-based)
9048          * @param {Number} y Y value for new position (coordinates are page-based)
9049          * @param {Number} width The new width
9050          * @param {Number} height The new height
9051          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9052          * @return {Roo.Element} this
9053          */
9054         setBounds : function(x, y, width, height, animate){
9055             if(!animate || !A){
9056                 this.setSize(width, height);
9057                 this.setLocation(x, y);
9058             }else{
9059                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9060                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9061                               this.preanim(arguments, 4), 'motion');
9062             }
9063             return this;
9064         },
9065
9066         /**
9067          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
9068          * @param {Roo.lib.Region} region The region to fill
9069          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9070          * @return {Roo.Element} this
9071          */
9072         setRegion : function(region, animate){
9073             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9074             return this;
9075         },
9076
9077         /**
9078          * Appends an event handler
9079          *
9080          * @param {String}   eventName     The type of event to append
9081          * @param {Function} fn        The method the event invokes
9082          * @param {Object} scope       (optional) The scope (this object) of the fn
9083          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9084          */
9085         addListener : function(eventName, fn, scope, options)
9086         {
9087             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9088                 this.addListener('touchstart', this.onTapHandler, this);
9089             }
9090             
9091             // we need to handle a special case where dom element is a svg element.
9092             // in this case we do not actua
9093             if (!this.dom) {
9094                 return;
9095             }
9096             
9097             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9098                 if (typeof(this.listeners[eventName]) == 'undefined') {
9099                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9100                 }
9101                 this.listeners[eventName].addListener(fn, scope, options);
9102                 return;
9103             }
9104             
9105                 
9106             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9107             
9108             
9109         },
9110         tapedTwice : false,
9111         onTapHandler : function(event)
9112         {
9113             if(!this.tapedTwice) {
9114                 this.tapedTwice = true;
9115                 var s = this;
9116                 setTimeout( function() {
9117                     s.tapedTwice = false;
9118                 }, 300 );
9119                 return;
9120             }
9121             event.preventDefault();
9122             var revent = new MouseEvent('dblclick',  {
9123                 view: window,
9124                 bubbles: true,
9125                 cancelable: true
9126             });
9127              
9128             this.dom.dispatchEvent(revent);
9129             //action on double tap goes below
9130              
9131         }, 
9132  
9133         /**
9134          * Removes an event handler from this element
9135          * @param {String} eventName the type of event to remove
9136          * @param {Function} fn the method the event invokes
9137          * @param {Function} scope (needed for svg fake listeners)
9138          * @return {Roo.Element} this
9139          */
9140         removeListener : function(eventName, fn, scope){
9141             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9142             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9143                 return this;
9144             }
9145             this.listeners[eventName].removeListener(fn, scope);
9146             return this;
9147         },
9148
9149         /**
9150          * Removes all previous added listeners from this element
9151          * @return {Roo.Element} this
9152          */
9153         removeAllListeners : function(){
9154             E.purgeElement(this.dom);
9155             this.listeners = {};
9156             return this;
9157         },
9158
9159         relayEvent : function(eventName, observable){
9160             this.on(eventName, function(e){
9161                 observable.fireEvent(eventName, e);
9162             });
9163         },
9164
9165         
9166         /**
9167          * Set the opacity of the element
9168          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9169          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9170          * @return {Roo.Element} this
9171          */
9172          setOpacity : function(opacity, animate){
9173             if(!animate || !A){
9174                 var s = this.dom.style;
9175                 if(Roo.isIE){
9176                     s.zoom = 1;
9177                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9178                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9179                 }else{
9180                     s.opacity = opacity;
9181                 }
9182             }else{
9183                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9184             }
9185             return this;
9186         },
9187
9188         /**
9189          * Gets the left X coordinate
9190          * @param {Boolean} local True to get the local css position instead of page coordinate
9191          * @return {Number}
9192          */
9193         getLeft : function(local){
9194             if(!local){
9195                 return this.getX();
9196             }else{
9197                 return parseInt(this.getStyle("left"), 10) || 0;
9198             }
9199         },
9200
9201         /**
9202          * Gets the right X coordinate of the element (element X position + element width)
9203          * @param {Boolean} local True to get the local css position instead of page coordinate
9204          * @return {Number}
9205          */
9206         getRight : function(local){
9207             if(!local){
9208                 return this.getX() + this.getWidth();
9209             }else{
9210                 return (this.getLeft(true) + this.getWidth()) || 0;
9211             }
9212         },
9213
9214         /**
9215          * Gets the top Y coordinate
9216          * @param {Boolean} local True to get the local css position instead of page coordinate
9217          * @return {Number}
9218          */
9219         getTop : function(local) {
9220             if(!local){
9221                 return this.getY();
9222             }else{
9223                 return parseInt(this.getStyle("top"), 10) || 0;
9224             }
9225         },
9226
9227         /**
9228          * Gets the bottom Y coordinate of the element (element Y position + element height)
9229          * @param {Boolean} local True to get the local css position instead of page coordinate
9230          * @return {Number}
9231          */
9232         getBottom : function(local){
9233             if(!local){
9234                 return this.getY() + this.getHeight();
9235             }else{
9236                 return (this.getTop(true) + this.getHeight()) || 0;
9237             }
9238         },
9239
9240         /**
9241         * Initializes positioning on this element. If a desired position is not passed, it will make the
9242         * the element positioned relative IF it is not already positioned.
9243         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9244         * @param {Number} zIndex (optional) The zIndex to apply
9245         * @param {Number} x (optional) Set the page X position
9246         * @param {Number} y (optional) Set the page Y position
9247         */
9248         position : function(pos, zIndex, x, y){
9249             if(!pos){
9250                if(this.getStyle('position') == 'static'){
9251                    this.setStyle('position', 'relative');
9252                }
9253             }else{
9254                 this.setStyle("position", pos);
9255             }
9256             if(zIndex){
9257                 this.setStyle("z-index", zIndex);
9258             }
9259             if(x !== undefined && y !== undefined){
9260                 this.setXY([x, y]);
9261             }else if(x !== undefined){
9262                 this.setX(x);
9263             }else if(y !== undefined){
9264                 this.setY(y);
9265             }
9266         },
9267
9268         /**
9269         * Clear positioning back to the default when the document was loaded
9270         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9271         * @return {Roo.Element} this
9272          */
9273         clearPositioning : function(value){
9274             value = value ||'';
9275             this.setStyle({
9276                 "left": value,
9277                 "right": value,
9278                 "top": value,
9279                 "bottom": value,
9280                 "z-index": "",
9281                 "position" : "static"
9282             });
9283             return this;
9284         },
9285
9286         /**
9287         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9288         * snapshot before performing an update and then restoring the element.
9289         * @return {Object}
9290         */
9291         getPositioning : function(){
9292             var l = this.getStyle("left");
9293             var t = this.getStyle("top");
9294             return {
9295                 "position" : this.getStyle("position"),
9296                 "left" : l,
9297                 "right" : l ? "" : this.getStyle("right"),
9298                 "top" : t,
9299                 "bottom" : t ? "" : this.getStyle("bottom"),
9300                 "z-index" : this.getStyle("z-index")
9301             };
9302         },
9303
9304         /**
9305          * Gets the width of the border(s) for the specified side(s)
9306          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9307          * passing lr would get the border (l)eft width + the border (r)ight width.
9308          * @return {Number} The width of the sides passed added together
9309          */
9310         getBorderWidth : function(side){
9311             return this.addStyles(side, El.borders);
9312         },
9313
9314         /**
9315          * Gets the width of the padding(s) for the specified side(s)
9316          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9317          * passing lr would get the padding (l)eft + the padding (r)ight.
9318          * @return {Number} The padding of the sides passed added together
9319          */
9320         getPadding : function(side){
9321             return this.addStyles(side, El.paddings);
9322         },
9323
9324         /**
9325         * Set positioning with an object returned by getPositioning().
9326         * @param {Object} posCfg
9327         * @return {Roo.Element} this
9328          */
9329         setPositioning : function(pc){
9330             this.applyStyles(pc);
9331             if(pc.right == "auto"){
9332                 this.dom.style.right = "";
9333             }
9334             if(pc.bottom == "auto"){
9335                 this.dom.style.bottom = "";
9336             }
9337             return this;
9338         },
9339
9340         // private
9341         fixDisplay : function(){
9342             if(this.getStyle("display") == "none"){
9343                 this.setStyle("visibility", "hidden");
9344                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9345                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9346                     this.setStyle("display", "block");
9347                 }
9348             }
9349         },
9350
9351         /**
9352          * Quick set left and top adding default units
9353          * @param {String} left The left CSS property value
9354          * @param {String} top The top CSS property value
9355          * @return {Roo.Element} this
9356          */
9357          setLeftTop : function(left, top){
9358             this.dom.style.left = this.addUnits(left);
9359             this.dom.style.top = this.addUnits(top);
9360             return this;
9361         },
9362
9363         /**
9364          * Move this element relative to its current position.
9365          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9366          * @param {Number} distance How far to move the element in pixels
9367          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9368          * @return {Roo.Element} this
9369          */
9370          move : function(direction, distance, animate){
9371             var xy = this.getXY();
9372             direction = direction.toLowerCase();
9373             switch(direction){
9374                 case "l":
9375                 case "left":
9376                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9377                     break;
9378                case "r":
9379                case "right":
9380                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9381                     break;
9382                case "t":
9383                case "top":
9384                case "up":
9385                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9386                     break;
9387                case "b":
9388                case "bottom":
9389                case "down":
9390                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9391                     break;
9392             }
9393             return this;
9394         },
9395
9396         /**
9397          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9398          * @return {Roo.Element} this
9399          */
9400         clip : function(){
9401             if(!this.isClipped){
9402                this.isClipped = true;
9403                this.originalClip = {
9404                    "o": this.getStyle("overflow"),
9405                    "x": this.getStyle("overflow-x"),
9406                    "y": this.getStyle("overflow-y")
9407                };
9408                this.setStyle("overflow", "hidden");
9409                this.setStyle("overflow-x", "hidden");
9410                this.setStyle("overflow-y", "hidden");
9411             }
9412             return this;
9413         },
9414
9415         /**
9416          *  Return clipping (overflow) to original clipping before clip() was called
9417          * @return {Roo.Element} this
9418          */
9419         unclip : function(){
9420             if(this.isClipped){
9421                 this.isClipped = false;
9422                 var o = this.originalClip;
9423                 if(o.o){this.setStyle("overflow", o.o);}
9424                 if(o.x){this.setStyle("overflow-x", o.x);}
9425                 if(o.y){this.setStyle("overflow-y", o.y);}
9426             }
9427             return this;
9428         },
9429
9430
9431         /**
9432          * Gets the x,y coordinates specified by the anchor position on the element.
9433          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9434          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9435          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9436          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9437          * @return {Array} [x, y] An array containing the element's x and y coordinates
9438          */
9439         getAnchorXY : function(anchor, local, s){
9440             //Passing a different size is useful for pre-calculating anchors,
9441             //especially for anchored animations that change the el size.
9442
9443             var w, h, vp = false;
9444             if(!s){
9445                 var d = this.dom;
9446                 if(d == document.body || d == document){
9447                     vp = true;
9448                     w = D.getViewWidth(); h = D.getViewHeight();
9449                 }else{
9450                     w = this.getWidth(); h = this.getHeight();
9451                 }
9452             }else{
9453                 w = s.width;  h = s.height;
9454             }
9455             var x = 0, y = 0, r = Math.round;
9456             switch((anchor || "tl").toLowerCase()){
9457                 case "c":
9458                     x = r(w*.5);
9459                     y = r(h*.5);
9460                 break;
9461                 case "t":
9462                     x = r(w*.5);
9463                     y = 0;
9464                 break;
9465                 case "l":
9466                     x = 0;
9467                     y = r(h*.5);
9468                 break;
9469                 case "r":
9470                     x = w;
9471                     y = r(h*.5);
9472                 break;
9473                 case "b":
9474                     x = r(w*.5);
9475                     y = h;
9476                 break;
9477                 case "tl":
9478                     x = 0;
9479                     y = 0;
9480                 break;
9481                 case "bl":
9482                     x = 0;
9483                     y = h;
9484                 break;
9485                 case "br":
9486                     x = w;
9487                     y = h;
9488                 break;
9489                 case "tr":
9490                     x = w;
9491                     y = 0;
9492                 break;
9493             }
9494             if(local === true){
9495                 return [x, y];
9496             }
9497             if(vp){
9498                 var sc = this.getScroll();
9499                 return [x + sc.left, y + sc.top];
9500             }
9501             //Add the element's offset xy
9502             var o = this.getXY();
9503             return [x+o[0], y+o[1]];
9504         },
9505
9506         /**
9507          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9508          * supported position values.
9509          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9510          * @param {String} position The position to align to.
9511          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9512          * @return {Array} [x, y]
9513          */
9514         getAlignToXY : function(el, p, o)
9515         {
9516             el = Roo.get(el);
9517             var d = this.dom;
9518             if(!el.dom){
9519                 throw "Element.alignTo with an element that doesn't exist";
9520             }
9521             var c = false; //constrain to viewport
9522             var p1 = "", p2 = "";
9523             o = o || [0,0];
9524
9525             if(!p){
9526                 p = "tl-bl";
9527             }else if(p == "?"){
9528                 p = "tl-bl?";
9529             }else if(p.indexOf("-") == -1){
9530                 p = "tl-" + p;
9531             }
9532             p = p.toLowerCase();
9533             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9534             if(!m){
9535                throw "Element.alignTo with an invalid alignment " + p;
9536             }
9537             p1 = m[1]; p2 = m[2]; c = !!m[3];
9538
9539             //Subtract the aligned el's internal xy from the target's offset xy
9540             //plus custom offset to get the aligned el's new offset xy
9541             var a1 = this.getAnchorXY(p1, true);
9542             var a2 = el.getAnchorXY(p2, false);
9543             var x = a2[0] - a1[0] + o[0];
9544             var y = a2[1] - a1[1] + o[1];
9545             if(c){
9546                 //constrain the aligned el to viewport if necessary
9547                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9548                 // 5px of margin for ie
9549                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9550
9551                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9552                 //perpendicular to the vp border, allow the aligned el to slide on that border,
9553                 //otherwise swap the aligned el to the opposite border of the target.
9554                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9555                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9556                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
9557                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9558
9559                var doc = document;
9560                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9561                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9562
9563                if((x+w) > dw + scrollX){
9564                     x = swapX ? r.left-w : dw+scrollX-w;
9565                 }
9566                if(x < scrollX){
9567                    x = swapX ? r.right : scrollX;
9568                }
9569                if((y+h) > dh + scrollY){
9570                     y = swapY ? r.top-h : dh+scrollY-h;
9571                 }
9572                if (y < scrollY){
9573                    y = swapY ? r.bottom : scrollY;
9574                }
9575             }
9576             return [x,y];
9577         },
9578
9579         // private
9580         getConstrainToXY : function(){
9581             var os = {top:0, left:0, bottom:0, right: 0};
9582
9583             return function(el, local, offsets, proposedXY){
9584                 el = Roo.get(el);
9585                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9586
9587                 var vw, vh, vx = 0, vy = 0;
9588                 if(el.dom == document.body || el.dom == document){
9589                     vw = Roo.lib.Dom.getViewWidth();
9590                     vh = Roo.lib.Dom.getViewHeight();
9591                 }else{
9592                     vw = el.dom.clientWidth;
9593                     vh = el.dom.clientHeight;
9594                     if(!local){
9595                         var vxy = el.getXY();
9596                         vx = vxy[0];
9597                         vy = vxy[1];
9598                     }
9599                 }
9600
9601                 var s = el.getScroll();
9602
9603                 vx += offsets.left + s.left;
9604                 vy += offsets.top + s.top;
9605
9606                 vw -= offsets.right;
9607                 vh -= offsets.bottom;
9608
9609                 var vr = vx+vw;
9610                 var vb = vy+vh;
9611
9612                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9613                 var x = xy[0], y = xy[1];
9614                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9615
9616                 // only move it if it needs it
9617                 var moved = false;
9618
9619                 // first validate right/bottom
9620                 if((x + w) > vr){
9621                     x = vr - w;
9622                     moved = true;
9623                 }
9624                 if((y + h) > vb){
9625                     y = vb - h;
9626                     moved = true;
9627                 }
9628                 // then make sure top/left isn't negative
9629                 if(x < vx){
9630                     x = vx;
9631                     moved = true;
9632                 }
9633                 if(y < vy){
9634                     y = vy;
9635                     moved = true;
9636                 }
9637                 return moved ? [x, y] : false;
9638             };
9639         }(),
9640
9641         // private
9642         adjustForConstraints : function(xy, parent, offsets){
9643             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
9644         },
9645
9646         /**
9647          * Aligns this element with another element relative to the specified anchor points. If the other element is the
9648          * document it aligns it to the viewport.
9649          * The position parameter is optional, and can be specified in any one of the following formats:
9650          * <ul>
9651          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9652          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9653          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
9654          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
9655          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
9656          *       element's anchor point, and the second value is used as the target's anchor point.</li>
9657          * </ul>
9658          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
9659          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9660          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
9661          * that specified in order to enforce the viewport constraints.
9662          * Following are all of the supported anchor positions:
9663     <pre>
9664     Value  Description
9665     -----  -----------------------------
9666     tl     The top left corner (default)
9667     t      The center of the top edge
9668     tr     The top right corner
9669     l      The center of the left edge
9670     c      In the center of the element
9671     r      The center of the right edge
9672     bl     The bottom left corner
9673     b      The center of the bottom edge
9674     br     The bottom right corner
9675     </pre>
9676     Example Usage:
9677     <pre><code>
9678     // align el to other-el using the default positioning ("tl-bl", non-constrained)
9679     el.alignTo("other-el");
9680
9681     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9682     el.alignTo("other-el", "tr?");
9683
9684     // align the bottom right corner of el with the center left edge of other-el
9685     el.alignTo("other-el", "br-l?");
9686
9687     // align the center of el with the bottom left corner of other-el and
9688     // adjust the x position by -6 pixels (and the y position by 0)
9689     el.alignTo("other-el", "c-bl", [-6, 0]);
9690     </code></pre>
9691          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9692          * @param {String} position The position to align to.
9693          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9694          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9695          * @return {Roo.Element} this
9696          */
9697         alignTo : function(element, position, offsets, animate){
9698             var xy = this.getAlignToXY(element, position, offsets);
9699             this.setXY(xy, this.preanim(arguments, 3));
9700             return this;
9701         },
9702
9703         /**
9704          * Anchors an element to another element and realigns it when the window is resized.
9705          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9706          * @param {String} position The position to align to.
9707          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9708          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
9709          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
9710          * is a number, it is used as the buffer delay (defaults to 50ms).
9711          * @param {Function} callback The function to call after the animation finishes
9712          * @return {Roo.Element} this
9713          */
9714         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
9715             var action = function(){
9716                 this.alignTo(el, alignment, offsets, animate);
9717                 Roo.callback(callback, this);
9718             };
9719             Roo.EventManager.onWindowResize(action, this);
9720             var tm = typeof monitorScroll;
9721             if(tm != 'undefined'){
9722                 Roo.EventManager.on(window, 'scroll', action, this,
9723                     {buffer: tm == 'number' ? monitorScroll : 50});
9724             }
9725             action.call(this); // align immediately
9726             return this;
9727         },
9728         /**
9729          * Clears any opacity settings from this element. Required in some cases for IE.
9730          * @return {Roo.Element} this
9731          */
9732         clearOpacity : function(){
9733             if (window.ActiveXObject) {
9734                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
9735                     this.dom.style.filter = "";
9736                 }
9737             } else {
9738                 this.dom.style.opacity = "";
9739                 this.dom.style["-moz-opacity"] = "";
9740                 this.dom.style["-khtml-opacity"] = "";
9741             }
9742             return this;
9743         },
9744
9745         /**
9746          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9747          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9748          * @return {Roo.Element} this
9749          */
9750         hide : function(animate){
9751             this.setVisible(false, this.preanim(arguments, 0));
9752             return this;
9753         },
9754
9755         /**
9756         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9757         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9758          * @return {Roo.Element} this
9759          */
9760         show : function(animate){
9761             this.setVisible(true, this.preanim(arguments, 0));
9762             return this;
9763         },
9764
9765         /**
9766          * @private Test if size has a unit, otherwise appends the default
9767          */
9768         addUnits : function(size){
9769             return Roo.Element.addUnits(size, this.defaultUnit);
9770         },
9771
9772         /**
9773          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
9774          * @return {Roo.Element} this
9775          */
9776         beginMeasure : function(){
9777             var el = this.dom;
9778             if(el.offsetWidth || el.offsetHeight){
9779                 return this; // offsets work already
9780             }
9781             var changed = [];
9782             var p = this.dom, b = document.body; // start with this element
9783             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
9784                 var pe = Roo.get(p);
9785                 if(pe.getStyle('display') == 'none'){
9786                     changed.push({el: p, visibility: pe.getStyle("visibility")});
9787                     p.style.visibility = "hidden";
9788                     p.style.display = "block";
9789                 }
9790                 p = p.parentNode;
9791             }
9792             this._measureChanged = changed;
9793             return this;
9794
9795         },
9796
9797         /**
9798          * Restores displays to before beginMeasure was called
9799          * @return {Roo.Element} this
9800          */
9801         endMeasure : function(){
9802             var changed = this._measureChanged;
9803             if(changed){
9804                 for(var i = 0, len = changed.length; i < len; i++) {
9805                     var r = changed[i];
9806                     r.el.style.visibility = r.visibility;
9807                     r.el.style.display = "none";
9808                 }
9809                 this._measureChanged = null;
9810             }
9811             return this;
9812         },
9813
9814         /**
9815         * Update the innerHTML of this element, optionally searching for and processing scripts
9816         * @param {String} html The new HTML
9817         * @param {Boolean} loadScripts (optional) true to look for and process scripts
9818         * @param {Function} callback For async script loading you can be noticed when the update completes
9819         * @return {Roo.Element} this
9820          */
9821         update : function(html, loadScripts, callback){
9822             if(typeof html == "undefined"){
9823                 html = "";
9824             }
9825             if(loadScripts !== true){
9826                 this.dom.innerHTML = html;
9827                 if(typeof callback == "function"){
9828                     callback();
9829                 }
9830                 return this;
9831             }
9832             var id = Roo.id();
9833             var dom = this.dom;
9834
9835             html += '<span id="' + id + '"></span>';
9836
9837             E.onAvailable(id, function(){
9838                 var hd = document.getElementsByTagName("head")[0];
9839                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
9840                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
9841                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
9842
9843                 var match;
9844                 while(match = re.exec(html)){
9845                     var attrs = match[1];
9846                     var srcMatch = attrs ? attrs.match(srcRe) : false;
9847                     if(srcMatch && srcMatch[2]){
9848                        var s = document.createElement("script");
9849                        s.src = srcMatch[2];
9850                        var typeMatch = attrs.match(typeRe);
9851                        if(typeMatch && typeMatch[2]){
9852                            s.type = typeMatch[2];
9853                        }
9854                        hd.appendChild(s);
9855                     }else if(match[2] && match[2].length > 0){
9856                         if(window.execScript) {
9857                            window.execScript(match[2]);
9858                         } else {
9859                             /**
9860                              * eval:var:id
9861                              * eval:var:dom
9862                              * eval:var:html
9863                              * 
9864                              */
9865                            window.eval(match[2]);
9866                         }
9867                     }
9868                 }
9869                 var el = document.getElementById(id);
9870                 if(el){el.parentNode.removeChild(el);}
9871                 if(typeof callback == "function"){
9872                     callback();
9873                 }
9874             });
9875             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9876             return this;
9877         },
9878
9879         /**
9880          * Direct access to the UpdateManager update() method (takes the same parameters).
9881          * @param {String/Function} url The url for this request or a function to call to get the url
9882          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
9883          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9884          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
9885          * @return {Roo.Element} this
9886          */
9887         load : function(){
9888             var um = this.getUpdateManager();
9889             um.update.apply(um, arguments);
9890             return this;
9891         },
9892
9893         /**
9894         * Gets this element's UpdateManager
9895         * @return {Roo.UpdateManager} The UpdateManager
9896         */
9897         getUpdateManager : function(){
9898             if(!this.updateManager){
9899                 this.updateManager = new Roo.UpdateManager(this);
9900             }
9901             return this.updateManager;
9902         },
9903
9904         /**
9905          * Disables text selection for this element (normalized across browsers)
9906          * @return {Roo.Element} this
9907          */
9908         unselectable : function(){
9909             this.dom.unselectable = "on";
9910             this.swallowEvent("selectstart", true);
9911             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9912             this.addClass("x-unselectable");
9913             return this;
9914         },
9915
9916         /**
9917         * Calculates the x, y to center this element on the screen
9918         * @return {Array} The x, y values [x, y]
9919         */
9920         getCenterXY : function(){
9921             return this.getAlignToXY(document, 'c-c');
9922         },
9923
9924         /**
9925         * Centers the Element in either the viewport, or another Element.
9926         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9927         */
9928         center : function(centerIn){
9929             this.alignTo(centerIn || document, 'c-c');
9930             return this;
9931         },
9932
9933         /**
9934          * Tests various css rules/browsers to determine if this element uses a border box
9935          * @return {Boolean}
9936          */
9937         isBorderBox : function(){
9938             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9939         },
9940
9941         /**
9942          * Return a box {x, y, width, height} that can be used to set another elements
9943          * size/location to match this element.
9944          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9945          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9946          * @return {Object} box An object in the format {x, y, width, height}
9947          */
9948         getBox : function(contentBox, local){
9949             var xy;
9950             if(!local){
9951                 xy = this.getXY();
9952             }else{
9953                 var left = parseInt(this.getStyle("left"), 10) || 0;
9954                 var top = parseInt(this.getStyle("top"), 10) || 0;
9955                 xy = [left, top];
9956             }
9957             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9958             if(!contentBox){
9959                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9960             }else{
9961                 var l = this.getBorderWidth("l")+this.getPadding("l");
9962                 var r = this.getBorderWidth("r")+this.getPadding("r");
9963                 var t = this.getBorderWidth("t")+this.getPadding("t");
9964                 var b = this.getBorderWidth("b")+this.getPadding("b");
9965                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
9966             }
9967             bx.right = bx.x + bx.width;
9968             bx.bottom = bx.y + bx.height;
9969             return bx;
9970         },
9971
9972         /**
9973          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9974          for more information about the sides.
9975          * @param {String} sides
9976          * @return {Number}
9977          */
9978         getFrameWidth : function(sides, onlyContentBox){
9979             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9980         },
9981
9982         /**
9983          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
9984          * @param {Object} box The box to fill {x, y, width, height}
9985          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9986          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9987          * @return {Roo.Element} this
9988          */
9989         setBox : function(box, adjust, animate){
9990             var w = box.width, h = box.height;
9991             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9992                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9993                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9994             }
9995             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9996             return this;
9997         },
9998
9999         /**
10000          * Forces the browser to repaint this element
10001          * @return {Roo.Element} this
10002          */
10003          repaint : function(){
10004             var dom = this.dom;
10005             this.addClass("x-repaint");
10006             setTimeout(function(){
10007                 Roo.get(dom).removeClass("x-repaint");
10008             }, 1);
10009             return this;
10010         },
10011
10012         /**
10013          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10014          * then it returns the calculated width of the sides (see getPadding)
10015          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10016          * @return {Object/Number}
10017          */
10018         getMargins : function(side){
10019             if(!side){
10020                 return {
10021                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10022                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10023                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10024                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10025                 };
10026             }else{
10027                 return this.addStyles(side, El.margins);
10028              }
10029         },
10030
10031         // private
10032         addStyles : function(sides, styles){
10033             var val = 0, v, w;
10034             for(var i = 0, len = sides.length; i < len; i++){
10035                 v = this.getStyle(styles[sides.charAt(i)]);
10036                 if(v){
10037                      w = parseInt(v, 10);
10038                      if(w){ val += w; }
10039                 }
10040             }
10041             return val;
10042         },
10043
10044         /**
10045          * Creates a proxy element of this element
10046          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10047          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10048          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10049          * @return {Roo.Element} The new proxy element
10050          */
10051         createProxy : function(config, renderTo, matchBox){
10052             if(renderTo){
10053                 renderTo = Roo.getDom(renderTo);
10054             }else{
10055                 renderTo = document.body;
10056             }
10057             config = typeof config == "object" ?
10058                 config : {tag : "div", cls: config};
10059             var proxy = Roo.DomHelper.append(renderTo, config, true);
10060             if(matchBox){
10061                proxy.setBox(this.getBox());
10062             }
10063             return proxy;
10064         },
10065
10066         /**
10067          * Puts a mask over this element to disable user interaction. Requires core.css.
10068          * This method can only be applied to elements which accept child nodes.
10069          * @param {String} msg (optional) A message to display in the mask
10070          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10071          * @return {Element} The mask  element
10072          */
10073         mask : function(msg, msgCls)
10074         {
10075             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10076                 this.setStyle("position", "relative");
10077             }
10078             if(!this._mask){
10079                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10080             }
10081             
10082             this.addClass("x-masked");
10083             this._mask.setDisplayed(true);
10084             
10085             // we wander
10086             var z = 0;
10087             var dom = this.dom;
10088             while (dom && dom.style) {
10089                 if (!isNaN(parseInt(dom.style.zIndex))) {
10090                     z = Math.max(z, parseInt(dom.style.zIndex));
10091                 }
10092                 dom = dom.parentNode;
10093             }
10094             // if we are masking the body - then it hides everything..
10095             if (this.dom == document.body) {
10096                 z = 1000000;
10097                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10098                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10099             }
10100            
10101             if(typeof msg == 'string'){
10102                 if(!this._maskMsg){
10103                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10104                         cls: "roo-el-mask-msg", 
10105                         cn: [
10106                             {
10107                                 tag: 'i',
10108                                 cls: 'fa fa-spinner fa-spin'
10109                             },
10110                             {
10111                                 tag: 'div'
10112                             }   
10113                         ]
10114                     }, true);
10115                 }
10116                 var mm = this._maskMsg;
10117                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10118                 if (mm.dom.lastChild) { // weird IE issue?
10119                     mm.dom.lastChild.innerHTML = msg;
10120                 }
10121                 mm.setDisplayed(true);
10122                 mm.center(this);
10123                 mm.setStyle('z-index', z + 102);
10124             }
10125             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10126                 this._mask.setHeight(this.getHeight());
10127             }
10128             this._mask.setStyle('z-index', z + 100);
10129             
10130             return this._mask;
10131         },
10132
10133         /**
10134          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10135          * it is cached for reuse.
10136          */
10137         unmask : function(removeEl){
10138             if(this._mask){
10139                 if(removeEl === true){
10140                     this._mask.remove();
10141                     delete this._mask;
10142                     if(this._maskMsg){
10143                         this._maskMsg.remove();
10144                         delete this._maskMsg;
10145                     }
10146                 }else{
10147                     this._mask.setDisplayed(false);
10148                     if(this._maskMsg){
10149                         this._maskMsg.setDisplayed(false);
10150                     }
10151                 }
10152             }
10153             this.removeClass("x-masked");
10154         },
10155
10156         /**
10157          * Returns true if this element is masked
10158          * @return {Boolean}
10159          */
10160         isMasked : function(){
10161             return this._mask && this._mask.isVisible();
10162         },
10163
10164         /**
10165          * Creates an iframe shim for this element to keep selects and other windowed objects from
10166          * showing through.
10167          * @return {Roo.Element} The new shim element
10168          */
10169         createShim : function(){
10170             var el = document.createElement('iframe');
10171             el.frameBorder = 'no';
10172             el.className = 'roo-shim';
10173             if(Roo.isIE && Roo.isSecure){
10174                 el.src = Roo.SSL_SECURE_URL;
10175             }
10176             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10177             shim.autoBoxAdjust = false;
10178             return shim;
10179         },
10180
10181         /**
10182          * Removes this element from the DOM and deletes it from the cache
10183          */
10184         remove : function(){
10185             if(this.dom.parentNode){
10186                 this.dom.parentNode.removeChild(this.dom);
10187             }
10188             delete El.cache[this.dom.id];
10189         },
10190
10191         /**
10192          * Sets up event handlers to add and remove a css class when the mouse is over this element
10193          * @param {String} className
10194          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10195          * mouseout events for children elements
10196          * @return {Roo.Element} this
10197          */
10198         addClassOnOver : function(className, preventFlicker){
10199             this.on("mouseover", function(){
10200                 Roo.fly(this, '_internal').addClass(className);
10201             }, this.dom);
10202             var removeFn = function(e){
10203                 if(preventFlicker !== true || !e.within(this, true)){
10204                     Roo.fly(this, '_internal').removeClass(className);
10205                 }
10206             };
10207             this.on("mouseout", removeFn, this.dom);
10208             return this;
10209         },
10210
10211         /**
10212          * Sets up event handlers to add and remove a css class when this element has the focus
10213          * @param {String} className
10214          * @return {Roo.Element} this
10215          */
10216         addClassOnFocus : function(className){
10217             this.on("focus", function(){
10218                 Roo.fly(this, '_internal').addClass(className);
10219             }, this.dom);
10220             this.on("blur", function(){
10221                 Roo.fly(this, '_internal').removeClass(className);
10222             }, this.dom);
10223             return this;
10224         },
10225         /**
10226          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
10227          * @param {String} className
10228          * @return {Roo.Element} this
10229          */
10230         addClassOnClick : function(className){
10231             var dom = this.dom;
10232             this.on("mousedown", function(){
10233                 Roo.fly(dom, '_internal').addClass(className);
10234                 var d = Roo.get(document);
10235                 var fn = function(){
10236                     Roo.fly(dom, '_internal').removeClass(className);
10237                     d.removeListener("mouseup", fn);
10238                 };
10239                 d.on("mouseup", fn);
10240             });
10241             return this;
10242         },
10243
10244         /**
10245          * Stops the specified event from bubbling and optionally prevents the default action
10246          * @param {String} eventName
10247          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10248          * @return {Roo.Element} this
10249          */
10250         swallowEvent : function(eventName, preventDefault){
10251             var fn = function(e){
10252                 e.stopPropagation();
10253                 if(preventDefault){
10254                     e.preventDefault();
10255                 }
10256             };
10257             if(eventName instanceof Array){
10258                 for(var i = 0, len = eventName.length; i < len; i++){
10259                      this.on(eventName[i], fn);
10260                 }
10261                 return this;
10262             }
10263             this.on(eventName, fn);
10264             return this;
10265         },
10266
10267         /**
10268          * @private
10269          */
10270         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10271
10272         /**
10273          * Sizes this element to its parent element's dimensions performing
10274          * neccessary box adjustments.
10275          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10276          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10277          * @return {Roo.Element} this
10278          */
10279         fitToParent : function(monitorResize, targetParent) {
10280           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10281           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10282           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10283             return this;
10284           }
10285           var p = Roo.get(targetParent || this.dom.parentNode);
10286           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10287           if (monitorResize === true) {
10288             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10289             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10290           }
10291           return this;
10292         },
10293
10294         /**
10295          * Gets the next sibling, skipping text nodes
10296          * @return {HTMLElement} The next sibling or null
10297          */
10298         getNextSibling : function(){
10299             var n = this.dom.nextSibling;
10300             while(n && n.nodeType != 1){
10301                 n = n.nextSibling;
10302             }
10303             return n;
10304         },
10305
10306         /**
10307          * Gets the previous sibling, skipping text nodes
10308          * @return {HTMLElement} The previous sibling or null
10309          */
10310         getPrevSibling : function(){
10311             var n = this.dom.previousSibling;
10312             while(n && n.nodeType != 1){
10313                 n = n.previousSibling;
10314             }
10315             return n;
10316         },
10317
10318
10319         /**
10320          * Appends the passed element(s) to this element
10321          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10322          * @return {Roo.Element} this
10323          */
10324         appendChild: function(el){
10325             el = Roo.get(el);
10326             el.appendTo(this);
10327             return this;
10328         },
10329
10330         /**
10331          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10332          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10333          * automatically generated with the specified attributes.
10334          * @param {HTMLElement} insertBefore (optional) a child element of this element
10335          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10336          * @return {Roo.Element} The new child element
10337          */
10338         createChild: function(config, insertBefore, returnDom){
10339             config = config || {tag:'div'};
10340             if(insertBefore){
10341                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10342             }
10343             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10344         },
10345
10346         /**
10347          * Appends this element to the passed element
10348          * @param {String/HTMLElement/Element} el The new parent element
10349          * @return {Roo.Element} this
10350          */
10351         appendTo: function(el){
10352             el = Roo.getDom(el);
10353             el.appendChild(this.dom);
10354             return this;
10355         },
10356
10357         /**
10358          * Inserts this element before the passed element in the DOM
10359          * @param {String/HTMLElement/Element} el The element to insert before
10360          * @return {Roo.Element} this
10361          */
10362         insertBefore: function(el){
10363             el = Roo.getDom(el);
10364             el.parentNode.insertBefore(this.dom, el);
10365             return this;
10366         },
10367
10368         /**
10369          * Inserts this element after the passed element in the DOM
10370          * @param {String/HTMLElement/Element} el The element to insert after
10371          * @return {Roo.Element} this
10372          */
10373         insertAfter: function(el){
10374             el = Roo.getDom(el);
10375             el.parentNode.insertBefore(this.dom, el.nextSibling);
10376             return this;
10377         },
10378
10379         /**
10380          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10381          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10382          * @return {Roo.Element} The new child
10383          */
10384         insertFirst: function(el, returnDom){
10385             el = el || {};
10386             if(typeof el == 'object' && !el.nodeType){ // dh config
10387                 return this.createChild(el, this.dom.firstChild, returnDom);
10388             }else{
10389                 el = Roo.getDom(el);
10390                 this.dom.insertBefore(el, this.dom.firstChild);
10391                 return !returnDom ? Roo.get(el) : el;
10392             }
10393         },
10394
10395         /**
10396          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10397          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10398          * @param {String} where (optional) 'before' or 'after' defaults to before
10399          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10400          * @return {Roo.Element} the inserted Element
10401          */
10402         insertSibling: function(el, where, returnDom){
10403             where = where ? where.toLowerCase() : 'before';
10404             el = el || {};
10405             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10406
10407             if(typeof el == 'object' && !el.nodeType){ // dh config
10408                 if(where == 'after' && !this.dom.nextSibling){
10409                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10410                 }else{
10411                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10412                 }
10413
10414             }else{
10415                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10416                             where == 'before' ? this.dom : this.dom.nextSibling);
10417                 if(!returnDom){
10418                     rt = Roo.get(rt);
10419                 }
10420             }
10421             return rt;
10422         },
10423
10424         /**
10425          * Creates and wraps this element with another element
10426          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10427          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10428          * @return {HTMLElement/Element} The newly created wrapper element
10429          */
10430         wrap: function(config, returnDom){
10431             if(!config){
10432                 config = {tag: "div"};
10433             }
10434             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10435             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10436             return newEl;
10437         },
10438
10439         /**
10440          * Replaces the passed element with this element
10441          * @param {String/HTMLElement/Element} el The element to replace
10442          * @return {Roo.Element} this
10443          */
10444         replace: function(el){
10445             el = Roo.get(el);
10446             this.insertBefore(el);
10447             el.remove();
10448             return this;
10449         },
10450
10451         /**
10452          * Inserts an html fragment into this element
10453          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10454          * @param {String} html The HTML fragment
10455          * @param {Boolean} returnEl True to return an Roo.Element
10456          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10457          */
10458         insertHtml : function(where, html, returnEl){
10459             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10460             return returnEl ? Roo.get(el) : el;
10461         },
10462
10463         /**
10464          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10465          * @param {Object} o The object with the attributes
10466          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10467          * @return {Roo.Element} this
10468          */
10469         set : function(o, useSet){
10470             var el = this.dom;
10471             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10472             for(var attr in o){
10473                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10474                 if(attr=="cls"){
10475                     el.className = o["cls"];
10476                 }else{
10477                     if(useSet) {
10478                         el.setAttribute(attr, o[attr]);
10479                     } else {
10480                         el[attr] = o[attr];
10481                     }
10482                 }
10483             }
10484             if(o.style){
10485                 Roo.DomHelper.applyStyles(el, o.style);
10486             }
10487             return this;
10488         },
10489
10490         /**
10491          * Convenience method for constructing a KeyMap
10492          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
10493          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10494          * @param {Function} fn The function to call
10495          * @param {Object} scope (optional) The scope of the function
10496          * @return {Roo.KeyMap} The KeyMap created
10497          */
10498         addKeyListener : function(key, fn, scope){
10499             var config;
10500             if(typeof key != "object" || key instanceof Array){
10501                 config = {
10502                     key: key,
10503                     fn: fn,
10504                     scope: scope
10505                 };
10506             }else{
10507                 config = {
10508                     key : key.key,
10509                     shift : key.shift,
10510                     ctrl : key.ctrl,
10511                     alt : key.alt,
10512                     fn: fn,
10513                     scope: scope
10514                 };
10515             }
10516             return new Roo.KeyMap(this, config);
10517         },
10518
10519         /**
10520          * Creates a KeyMap for this element
10521          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10522          * @return {Roo.KeyMap} The KeyMap created
10523          */
10524         addKeyMap : function(config){
10525             return new Roo.KeyMap(this, config);
10526         },
10527
10528         /**
10529          * Returns true if this element is scrollable.
10530          * @return {Boolean}
10531          */
10532          isScrollable : function(){
10533             var dom = this.dom;
10534             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10535         },
10536
10537         /**
10538          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
10539          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10540          * @param {Number} value The new scroll value
10541          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10542          * @return {Element} this
10543          */
10544
10545         scrollTo : function(side, value, animate){
10546             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10547             if(!animate || !A){
10548                 this.dom[prop] = value;
10549             }else{
10550                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10551                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10552             }
10553             return this;
10554         },
10555
10556         /**
10557          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10558          * within this element's scrollable range.
10559          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10560          * @param {Number} distance How far to scroll the element in pixels
10561          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10562          * @return {Boolean} Returns true if a scroll was triggered or false if the element
10563          * was scrolled as far as it could go.
10564          */
10565          scroll : function(direction, distance, animate){
10566              if(!this.isScrollable()){
10567                  return;
10568              }
10569              var el = this.dom;
10570              var l = el.scrollLeft, t = el.scrollTop;
10571              var w = el.scrollWidth, h = el.scrollHeight;
10572              var cw = el.clientWidth, ch = el.clientHeight;
10573              direction = direction.toLowerCase();
10574              var scrolled = false;
10575              var a = this.preanim(arguments, 2);
10576              switch(direction){
10577                  case "l":
10578                  case "left":
10579                      if(w - l > cw){
10580                          var v = Math.min(l + distance, w-cw);
10581                          this.scrollTo("left", v, a);
10582                          scrolled = true;
10583                      }
10584                      break;
10585                 case "r":
10586                 case "right":
10587                      if(l > 0){
10588                          var v = Math.max(l - distance, 0);
10589                          this.scrollTo("left", v, a);
10590                          scrolled = true;
10591                      }
10592                      break;
10593                 case "t":
10594                 case "top":
10595                 case "up":
10596                      if(t > 0){
10597                          var v = Math.max(t - distance, 0);
10598                          this.scrollTo("top", v, a);
10599                          scrolled = true;
10600                      }
10601                      break;
10602                 case "b":
10603                 case "bottom":
10604                 case "down":
10605                      if(h - t > ch){
10606                          var v = Math.min(t + distance, h-ch);
10607                          this.scrollTo("top", v, a);
10608                          scrolled = true;
10609                      }
10610                      break;
10611              }
10612              return scrolled;
10613         },
10614
10615         /**
10616          * Translates the passed page coordinates into left/top css values for this element
10617          * @param {Number/Array} x The page x or an array containing [x, y]
10618          * @param {Number} y The page y
10619          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10620          */
10621         translatePoints : function(x, y){
10622             if(typeof x == 'object' || x instanceof Array){
10623                 y = x[1]; x = x[0];
10624             }
10625             var p = this.getStyle('position');
10626             var o = this.getXY();
10627
10628             var l = parseInt(this.getStyle('left'), 10);
10629             var t = parseInt(this.getStyle('top'), 10);
10630
10631             if(isNaN(l)){
10632                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10633             }
10634             if(isNaN(t)){
10635                 t = (p == "relative") ? 0 : this.dom.offsetTop;
10636             }
10637
10638             return {left: (x - o[0] + l), top: (y - o[1] + t)};
10639         },
10640
10641         /**
10642          * Returns the current scroll position of the element.
10643          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10644          */
10645         getScroll : function(){
10646             var d = this.dom, doc = document;
10647             if(d == doc || d == doc.body){
10648                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10649                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10650                 return {left: l, top: t};
10651             }else{
10652                 return {left: d.scrollLeft, top: d.scrollTop};
10653             }
10654         },
10655
10656         /**
10657          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10658          * are convert to standard 6 digit hex color.
10659          * @param {String} attr The css attribute
10660          * @param {String} defaultValue The default value to use when a valid color isn't found
10661          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10662          * YUI color anims.
10663          */
10664         getColor : function(attr, defaultValue, prefix){
10665             var v = this.getStyle(attr);
10666             if(!v || v == "transparent" || v == "inherit") {
10667                 return defaultValue;
10668             }
10669             var color = typeof prefix == "undefined" ? "#" : prefix;
10670             if(v.substr(0, 4) == "rgb("){
10671                 var rvs = v.slice(4, v.length -1).split(",");
10672                 for(var i = 0; i < 3; i++){
10673                     var h = parseInt(rvs[i]).toString(16);
10674                     if(h < 16){
10675                         h = "0" + h;
10676                     }
10677                     color += h;
10678                 }
10679             } else {
10680                 if(v.substr(0, 1) == "#"){
10681                     if(v.length == 4) {
10682                         for(var i = 1; i < 4; i++){
10683                             var c = v.charAt(i);
10684                             color +=  c + c;
10685                         }
10686                     }else if(v.length == 7){
10687                         color += v.substr(1);
10688                     }
10689                 }
10690             }
10691             return(color.length > 5 ? color.toLowerCase() : defaultValue);
10692         },
10693
10694         /**
10695          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
10696          * gradient background, rounded corners and a 4-way shadow.
10697          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
10698          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
10699          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
10700          * @return {Roo.Element} this
10701          */
10702         boxWrap : function(cls){
10703             cls = cls || 'x-box';
10704             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
10705             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
10706             return el;
10707         },
10708
10709         /**
10710          * Returns the value of a namespaced attribute from the element's underlying DOM node.
10711          * @param {String} namespace The namespace in which to look for the attribute
10712          * @param {String} name The attribute name
10713          * @return {String} The attribute value
10714          */
10715         getAttributeNS : Roo.isIE ? function(ns, name){
10716             var d = this.dom;
10717             var type = typeof d[ns+":"+name];
10718             if(type != 'undefined' && type != 'unknown'){
10719                 return d[ns+":"+name];
10720             }
10721             return d[name];
10722         } : function(ns, name){
10723             var d = this.dom;
10724             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
10725         },
10726         
10727         
10728         /**
10729          * Sets or Returns the value the dom attribute value
10730          * @param {String|Object} name The attribute name (or object to set multiple attributes)
10731          * @param {String} value (optional) The value to set the attribute to
10732          * @return {String} The attribute value
10733          */
10734         attr : function(name){
10735             if (arguments.length > 1) {
10736                 this.dom.setAttribute(name, arguments[1]);
10737                 return arguments[1];
10738             }
10739             if (typeof(name) == 'object') {
10740                 for(var i in name) {
10741                     this.attr(i, name[i]);
10742                 }
10743                 return name;
10744             }
10745             
10746             
10747             if (!this.dom.hasAttribute(name)) {
10748                 return undefined;
10749             }
10750             return this.dom.getAttribute(name);
10751         }
10752         
10753         
10754         
10755     };
10756
10757     var ep = El.prototype;
10758
10759     /**
10760      * Appends an event handler (Shorthand for addListener)
10761      * @param {String}   eventName     The type of event to append
10762      * @param {Function} fn        The method the event invokes
10763      * @param {Object} scope       (optional) The scope (this object) of the fn
10764      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
10765      * @method
10766      */
10767     ep.on = ep.addListener;
10768         // backwards compat
10769     ep.mon = ep.addListener;
10770
10771     /**
10772      * Removes an event handler from this element (shorthand for removeListener)
10773      * @param {String} eventName the type of event to remove
10774      * @param {Function} fn the method the event invokes
10775      * @return {Roo.Element} this
10776      * @method
10777      */
10778     ep.un = ep.removeListener;
10779
10780     /**
10781      * true to automatically adjust width and height settings for box-model issues (default to true)
10782      */
10783     ep.autoBoxAdjust = true;
10784
10785     // private
10786     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
10787
10788     // private
10789     El.addUnits = function(v, defaultUnit){
10790         if(v === "" || v == "auto"){
10791             return v;
10792         }
10793         if(v === undefined){
10794             return '';
10795         }
10796         if(typeof v == "number" || !El.unitPattern.test(v)){
10797             return v + (defaultUnit || 'px');
10798         }
10799         return v;
10800     };
10801
10802     // special markup used throughout Roo when box wrapping elements
10803     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
10804     /**
10805      * Visibility mode constant - Use visibility to hide element
10806      * @static
10807      * @type Number
10808      */
10809     El.VISIBILITY = 1;
10810     /**
10811      * Visibility mode constant - Use display to hide element
10812      * @static
10813      * @type Number
10814      */
10815     El.DISPLAY = 2;
10816
10817     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
10818     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
10819     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
10820
10821
10822
10823     /**
10824      * @private
10825      */
10826     El.cache = {};
10827
10828     var docEl;
10829
10830     /**
10831      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10832      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10833      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10834      * @return {Element} The Element object
10835      * @static
10836      */
10837     El.get = function(el){
10838         var ex, elm, id;
10839         if(!el){ return null; }
10840         if(typeof el == "string"){ // element id
10841             if(!(elm = document.getElementById(el))){
10842                 return null;
10843             }
10844             if(ex = El.cache[el]){
10845                 ex.dom = elm;
10846             }else{
10847                 ex = El.cache[el] = new El(elm);
10848             }
10849             return ex;
10850         }else if(el.tagName){ // dom element
10851             if(!(id = el.id)){
10852                 id = Roo.id(el);
10853             }
10854             if(ex = El.cache[id]){
10855                 ex.dom = el;
10856             }else{
10857                 ex = El.cache[id] = new El(el);
10858             }
10859             return ex;
10860         }else if(el instanceof El){
10861             if(el != docEl){
10862                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
10863                                                               // catch case where it hasn't been appended
10864                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10865             }
10866             return el;
10867         }else if(el.isComposite){
10868             return el;
10869         }else if(el instanceof Array){
10870             return El.select(el);
10871         }else if(el == document){
10872             // create a bogus element object representing the document object
10873             if(!docEl){
10874                 var f = function(){};
10875                 f.prototype = El.prototype;
10876                 docEl = new f();
10877                 docEl.dom = document;
10878             }
10879             return docEl;
10880         }
10881         return null;
10882     };
10883
10884     // private
10885     El.uncache = function(el){
10886         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10887             if(a[i]){
10888                 delete El.cache[a[i].id || a[i]];
10889             }
10890         }
10891     };
10892
10893     // private
10894     // Garbage collection - uncache elements/purge listeners on orphaned elements
10895     // so we don't hold a reference and cause the browser to retain them
10896     El.garbageCollect = function(){
10897         if(!Roo.enableGarbageCollector){
10898             clearInterval(El.collectorThread);
10899             return;
10900         }
10901         for(var eid in El.cache){
10902             var el = El.cache[eid], d = el.dom;
10903             // -------------------------------------------------------
10904             // Determining what is garbage:
10905             // -------------------------------------------------------
10906             // !d
10907             // dom node is null, definitely garbage
10908             // -------------------------------------------------------
10909             // !d.parentNode
10910             // no parentNode == direct orphan, definitely garbage
10911             // -------------------------------------------------------
10912             // !d.offsetParent && !document.getElementById(eid)
10913             // display none elements have no offsetParent so we will
10914             // also try to look it up by it's id. However, check
10915             // offsetParent first so we don't do unneeded lookups.
10916             // This enables collection of elements that are not orphans
10917             // directly, but somewhere up the line they have an orphan
10918             // parent.
10919             // -------------------------------------------------------
10920             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10921                 delete El.cache[eid];
10922                 if(d && Roo.enableListenerCollection){
10923                     E.purgeElement(d);
10924                 }
10925             }
10926         }
10927     }
10928     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10929
10930
10931     // dom is optional
10932     El.Flyweight = function(dom){
10933         this.dom = dom;
10934     };
10935     El.Flyweight.prototype = El.prototype;
10936
10937     El._flyweights = {};
10938     /**
10939      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10940      * the dom node can be overwritten by other code.
10941      * @param {String/HTMLElement} el The dom node or id
10942      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10943      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10944      * @static
10945      * @return {Element} The shared Element object
10946      */
10947     El.fly = function(el, named){
10948         named = named || '_global';
10949         el = Roo.getDom(el);
10950         if(!el){
10951             return null;
10952         }
10953         if(!El._flyweights[named]){
10954             El._flyweights[named] = new El.Flyweight();
10955         }
10956         El._flyweights[named].dom = el;
10957         return El._flyweights[named];
10958     };
10959
10960     /**
10961      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10962      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10963      * Shorthand of {@link Roo.Element#get}
10964      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10965      * @return {Element} The Element object
10966      * @member Roo
10967      * @method get
10968      */
10969     Roo.get = El.get;
10970     /**
10971      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10972      * the dom node can be overwritten by other code.
10973      * Shorthand of {@link Roo.Element#fly}
10974      * @param {String/HTMLElement} el The dom node or id
10975      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10976      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10977      * @static
10978      * @return {Element} The shared Element object
10979      * @member Roo
10980      * @method fly
10981      */
10982     Roo.fly = El.fly;
10983
10984     // speedy lookup for elements never to box adjust
10985     var noBoxAdjust = Roo.isStrict ? {
10986         select:1
10987     } : {
10988         input:1, select:1, textarea:1
10989     };
10990     if(Roo.isIE || Roo.isGecko){
10991         noBoxAdjust['button'] = 1;
10992     }
10993
10994
10995     Roo.EventManager.on(window, 'unload', function(){
10996         delete El.cache;
10997         delete El._flyweights;
10998     });
10999 })();
11000
11001
11002
11003
11004 if(Roo.DomQuery){
11005     Roo.Element.selectorFunction = Roo.DomQuery.select;
11006 }
11007
11008 Roo.Element.select = function(selector, unique, root){
11009     var els;
11010     if(typeof selector == "string"){
11011         els = Roo.Element.selectorFunction(selector, root);
11012     }else if(selector.length !== undefined){
11013         els = selector;
11014     }else{
11015         throw "Invalid selector";
11016     }
11017     if(unique === true){
11018         return new Roo.CompositeElement(els);
11019     }else{
11020         return new Roo.CompositeElementLite(els);
11021     }
11022 };
11023 /**
11024  * Selects elements based on the passed CSS selector to enable working on them as 1.
11025  * @param {String/Array} selector The CSS selector or an array of elements
11026  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11027  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11028  * @return {CompositeElementLite/CompositeElement}
11029  * @member Roo
11030  * @method select
11031  */
11032 Roo.select = Roo.Element.select;
11033
11034
11035
11036
11037
11038
11039
11040
11041
11042
11043
11044
11045
11046
11047 /*
11048  * Based on:
11049  * Ext JS Library 1.1.1
11050  * Copyright(c) 2006-2007, Ext JS, LLC.
11051  *
11052  * Originally Released Under LGPL - original licence link has changed is not relivant.
11053  *
11054  * Fork - LGPL
11055  * <script type="text/javascript">
11056  */
11057
11058
11059
11060 //Notifies Element that fx methods are available
11061 Roo.enableFx = true;
11062
11063 /**
11064  * @class Roo.Fx
11065  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11066  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11067  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11068  * Element effects to work.</p><br/>
11069  *
11070  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11071  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11072  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11073  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11074  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11075  * expected results and should be done with care.</p><br/>
11076  *
11077  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11078  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11079 <pre>
11080 Value  Description
11081 -----  -----------------------------
11082 tl     The top left corner
11083 t      The center of the top edge
11084 tr     The top right corner
11085 l      The center of the left edge
11086 r      The center of the right edge
11087 bl     The bottom left corner
11088 b      The center of the bottom edge
11089 br     The bottom right corner
11090 </pre>
11091  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11092  * below are common options that can be passed to any Fx method.</b>
11093  * @cfg {Function} callback A function called when the effect is finished
11094  * @cfg {Object} scope The scope of the effect function
11095  * @cfg {String} easing A valid Easing value for the effect
11096  * @cfg {String} afterCls A css class to apply after the effect
11097  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11098  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11099  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11100  * effects that end with the element being visually hidden, ignored otherwise)
11101  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11102  * a function which returns such a specification that will be applied to the Element after the effect finishes
11103  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11104  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
11105  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11106  */
11107 Roo.Fx = {
11108         /**
11109          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11110          * origin for the slide effect.  This function automatically handles wrapping the element with
11111          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11112          * Usage:
11113          *<pre><code>
11114 // default: slide the element in from the top
11115 el.slideIn();
11116
11117 // custom: slide the element in from the right with a 2-second duration
11118 el.slideIn('r', { duration: 2 });
11119
11120 // common config options shown with default values
11121 el.slideIn('t', {
11122     easing: 'easeOut',
11123     duration: .5
11124 });
11125 </code></pre>
11126          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11127          * @param {Object} options (optional) Object literal with any of the Fx config options
11128          * @return {Roo.Element} The Element
11129          */
11130     slideIn : function(anchor, o){
11131         var el = this.getFxEl();
11132         o = o || {};
11133
11134         el.queueFx(o, function(){
11135
11136             anchor = anchor || "t";
11137
11138             // fix display to visibility
11139             this.fixDisplay();
11140
11141             // restore values after effect
11142             var r = this.getFxRestore();
11143             var b = this.getBox();
11144             // fixed size for slide
11145             this.setSize(b);
11146
11147             // wrap if needed
11148             var wrap = this.fxWrap(r.pos, o, "hidden");
11149
11150             var st = this.dom.style;
11151             st.visibility = "visible";
11152             st.position = "absolute";
11153
11154             // clear out temp styles after slide and unwrap
11155             var after = function(){
11156                 el.fxUnwrap(wrap, r.pos, o);
11157                 st.width = r.width;
11158                 st.height = r.height;
11159                 el.afterFx(o);
11160             };
11161             // time to calc the positions
11162             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11163
11164             switch(anchor.toLowerCase()){
11165                 case "t":
11166                     wrap.setSize(b.width, 0);
11167                     st.left = st.bottom = "0";
11168                     a = {height: bh};
11169                 break;
11170                 case "l":
11171                     wrap.setSize(0, b.height);
11172                     st.right = st.top = "0";
11173                     a = {width: bw};
11174                 break;
11175                 case "r":
11176                     wrap.setSize(0, b.height);
11177                     wrap.setX(b.right);
11178                     st.left = st.top = "0";
11179                     a = {width: bw, points: pt};
11180                 break;
11181                 case "b":
11182                     wrap.setSize(b.width, 0);
11183                     wrap.setY(b.bottom);
11184                     st.left = st.top = "0";
11185                     a = {height: bh, points: pt};
11186                 break;
11187                 case "tl":
11188                     wrap.setSize(0, 0);
11189                     st.right = st.bottom = "0";
11190                     a = {width: bw, height: bh};
11191                 break;
11192                 case "bl":
11193                     wrap.setSize(0, 0);
11194                     wrap.setY(b.y+b.height);
11195                     st.right = st.top = "0";
11196                     a = {width: bw, height: bh, points: pt};
11197                 break;
11198                 case "br":
11199                     wrap.setSize(0, 0);
11200                     wrap.setXY([b.right, b.bottom]);
11201                     st.left = st.top = "0";
11202                     a = {width: bw, height: bh, points: pt};
11203                 break;
11204                 case "tr":
11205                     wrap.setSize(0, 0);
11206                     wrap.setX(b.x+b.width);
11207                     st.left = st.bottom = "0";
11208                     a = {width: bw, height: bh, points: pt};
11209                 break;
11210             }
11211             this.dom.style.visibility = "visible";
11212             wrap.show();
11213
11214             arguments.callee.anim = wrap.fxanim(a,
11215                 o,
11216                 'motion',
11217                 .5,
11218                 'easeOut', after);
11219         });
11220         return this;
11221     },
11222     
11223         /**
11224          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11225          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11226          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11227          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11228          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11229          * Usage:
11230          *<pre><code>
11231 // default: slide the element out to the top
11232 el.slideOut();
11233
11234 // custom: slide the element out to the right with a 2-second duration
11235 el.slideOut('r', { duration: 2 });
11236
11237 // common config options shown with default values
11238 el.slideOut('t', {
11239     easing: 'easeOut',
11240     duration: .5,
11241     remove: false,
11242     useDisplay: false
11243 });
11244 </code></pre>
11245          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11246          * @param {Object} options (optional) Object literal with any of the Fx config options
11247          * @return {Roo.Element} The Element
11248          */
11249     slideOut : function(anchor, o){
11250         var el = this.getFxEl();
11251         o = o || {};
11252
11253         el.queueFx(o, function(){
11254
11255             anchor = anchor || "t";
11256
11257             // restore values after effect
11258             var r = this.getFxRestore();
11259             
11260             var b = this.getBox();
11261             // fixed size for slide
11262             this.setSize(b);
11263
11264             // wrap if needed
11265             var wrap = this.fxWrap(r.pos, o, "visible");
11266
11267             var st = this.dom.style;
11268             st.visibility = "visible";
11269             st.position = "absolute";
11270
11271             wrap.setSize(b);
11272
11273             var after = function(){
11274                 if(o.useDisplay){
11275                     el.setDisplayed(false);
11276                 }else{
11277                     el.hide();
11278                 }
11279
11280                 el.fxUnwrap(wrap, r.pos, o);
11281
11282                 st.width = r.width;
11283                 st.height = r.height;
11284
11285                 el.afterFx(o);
11286             };
11287
11288             var a, zero = {to: 0};
11289             switch(anchor.toLowerCase()){
11290                 case "t":
11291                     st.left = st.bottom = "0";
11292                     a = {height: zero};
11293                 break;
11294                 case "l":
11295                     st.right = st.top = "0";
11296                     a = {width: zero};
11297                 break;
11298                 case "r":
11299                     st.left = st.top = "0";
11300                     a = {width: zero, points: {to:[b.right, b.y]}};
11301                 break;
11302                 case "b":
11303                     st.left = st.top = "0";
11304                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11305                 break;
11306                 case "tl":
11307                     st.right = st.bottom = "0";
11308                     a = {width: zero, height: zero};
11309                 break;
11310                 case "bl":
11311                     st.right = st.top = "0";
11312                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11313                 break;
11314                 case "br":
11315                     st.left = st.top = "0";
11316                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11317                 break;
11318                 case "tr":
11319                     st.left = st.bottom = "0";
11320                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11321                 break;
11322             }
11323
11324             arguments.callee.anim = wrap.fxanim(a,
11325                 o,
11326                 'motion',
11327                 .5,
11328                 "easeOut", after);
11329         });
11330         return this;
11331     },
11332
11333         /**
11334          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11335          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11336          * The element must be removed from the DOM using the 'remove' config option if desired.
11337          * Usage:
11338          *<pre><code>
11339 // default
11340 el.puff();
11341
11342 // common config options shown with default values
11343 el.puff({
11344     easing: 'easeOut',
11345     duration: .5,
11346     remove: false,
11347     useDisplay: false
11348 });
11349 </code></pre>
11350          * @param {Object} options (optional) Object literal with any of the Fx config options
11351          * @return {Roo.Element} The Element
11352          */
11353     puff : function(o){
11354         var el = this.getFxEl();
11355         o = o || {};
11356
11357         el.queueFx(o, function(){
11358             this.clearOpacity();
11359             this.show();
11360
11361             // restore values after effect
11362             var r = this.getFxRestore();
11363             var st = this.dom.style;
11364
11365             var after = function(){
11366                 if(o.useDisplay){
11367                     el.setDisplayed(false);
11368                 }else{
11369                     el.hide();
11370                 }
11371
11372                 el.clearOpacity();
11373
11374                 el.setPositioning(r.pos);
11375                 st.width = r.width;
11376                 st.height = r.height;
11377                 st.fontSize = '';
11378                 el.afterFx(o);
11379             };
11380
11381             var width = this.getWidth();
11382             var height = this.getHeight();
11383
11384             arguments.callee.anim = this.fxanim({
11385                     width : {to: this.adjustWidth(width * 2)},
11386                     height : {to: this.adjustHeight(height * 2)},
11387                     points : {by: [-(width * .5), -(height * .5)]},
11388                     opacity : {to: 0},
11389                     fontSize: {to:200, unit: "%"}
11390                 },
11391                 o,
11392                 'motion',
11393                 .5,
11394                 "easeOut", after);
11395         });
11396         return this;
11397     },
11398
11399         /**
11400          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11401          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11402          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11403          * Usage:
11404          *<pre><code>
11405 // default
11406 el.switchOff();
11407
11408 // all config options shown with default values
11409 el.switchOff({
11410     easing: 'easeIn',
11411     duration: .3,
11412     remove: false,
11413     useDisplay: false
11414 });
11415 </code></pre>
11416          * @param {Object} options (optional) Object literal with any of the Fx config options
11417          * @return {Roo.Element} The Element
11418          */
11419     switchOff : function(o){
11420         var el = this.getFxEl();
11421         o = o || {};
11422
11423         el.queueFx(o, function(){
11424             this.clearOpacity();
11425             this.clip();
11426
11427             // restore values after effect
11428             var r = this.getFxRestore();
11429             var st = this.dom.style;
11430
11431             var after = function(){
11432                 if(o.useDisplay){
11433                     el.setDisplayed(false);
11434                 }else{
11435                     el.hide();
11436                 }
11437
11438                 el.clearOpacity();
11439                 el.setPositioning(r.pos);
11440                 st.width = r.width;
11441                 st.height = r.height;
11442
11443                 el.afterFx(o);
11444             };
11445
11446             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11447                 this.clearOpacity();
11448                 (function(){
11449                     this.fxanim({
11450                         height:{to:1},
11451                         points:{by:[0, this.getHeight() * .5]}
11452                     }, o, 'motion', 0.3, 'easeIn', after);
11453                 }).defer(100, this);
11454             });
11455         });
11456         return this;
11457     },
11458
11459     /**
11460      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11461      * changed using the "attr" config option) and then fading back to the original color. If no original
11462      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11463      * Usage:
11464 <pre><code>
11465 // default: highlight background to yellow
11466 el.highlight();
11467
11468 // custom: highlight foreground text to blue for 2 seconds
11469 el.highlight("0000ff", { attr: 'color', duration: 2 });
11470
11471 // common config options shown with default values
11472 el.highlight("ffff9c", {
11473     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11474     endColor: (current color) or "ffffff",
11475     easing: 'easeIn',
11476     duration: 1
11477 });
11478 </code></pre>
11479      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11480      * @param {Object} options (optional) Object literal with any of the Fx config options
11481      * @return {Roo.Element} The Element
11482      */ 
11483     highlight : function(color, o){
11484         var el = this.getFxEl();
11485         o = o || {};
11486
11487         el.queueFx(o, function(){
11488             color = color || "ffff9c";
11489             attr = o.attr || "backgroundColor";
11490
11491             this.clearOpacity();
11492             this.show();
11493
11494             var origColor = this.getColor(attr);
11495             var restoreColor = this.dom.style[attr];
11496             endColor = (o.endColor || origColor) || "ffffff";
11497
11498             var after = function(){
11499                 el.dom.style[attr] = restoreColor;
11500                 el.afterFx(o);
11501             };
11502
11503             var a = {};
11504             a[attr] = {from: color, to: endColor};
11505             arguments.callee.anim = this.fxanim(a,
11506                 o,
11507                 'color',
11508                 1,
11509                 'easeIn', after);
11510         });
11511         return this;
11512     },
11513
11514    /**
11515     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11516     * Usage:
11517 <pre><code>
11518 // default: a single light blue ripple
11519 el.frame();
11520
11521 // custom: 3 red ripples lasting 3 seconds total
11522 el.frame("ff0000", 3, { duration: 3 });
11523
11524 // common config options shown with default values
11525 el.frame("C3DAF9", 1, {
11526     duration: 1 //duration of entire animation (not each individual ripple)
11527     // Note: Easing is not configurable and will be ignored if included
11528 });
11529 </code></pre>
11530     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11531     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11532     * @param {Object} options (optional) Object literal with any of the Fx config options
11533     * @return {Roo.Element} The Element
11534     */
11535     frame : function(color, count, o){
11536         var el = this.getFxEl();
11537         o = o || {};
11538
11539         el.queueFx(o, function(){
11540             color = color || "#C3DAF9";
11541             if(color.length == 6){
11542                 color = "#" + color;
11543             }
11544             count = count || 1;
11545             duration = o.duration || 1;
11546             this.show();
11547
11548             var b = this.getBox();
11549             var animFn = function(){
11550                 var proxy = this.createProxy({
11551
11552                      style:{
11553                         visbility:"hidden",
11554                         position:"absolute",
11555                         "z-index":"35000", // yee haw
11556                         border:"0px solid " + color
11557                      }
11558                   });
11559                 var scale = Roo.isBorderBox ? 2 : 1;
11560                 proxy.animate({
11561                     top:{from:b.y, to:b.y - 20},
11562                     left:{from:b.x, to:b.x - 20},
11563                     borderWidth:{from:0, to:10},
11564                     opacity:{from:1, to:0},
11565                     height:{from:b.height, to:(b.height + (20*scale))},
11566                     width:{from:b.width, to:(b.width + (20*scale))}
11567                 }, duration, function(){
11568                     proxy.remove();
11569                 });
11570                 if(--count > 0){
11571                      animFn.defer((duration/2)*1000, this);
11572                 }else{
11573                     el.afterFx(o);
11574                 }
11575             };
11576             animFn.call(this);
11577         });
11578         return this;
11579     },
11580
11581    /**
11582     * Creates a pause before any subsequent queued effects begin.  If there are
11583     * no effects queued after the pause it will have no effect.
11584     * Usage:
11585 <pre><code>
11586 el.pause(1);
11587 </code></pre>
11588     * @param {Number} seconds The length of time to pause (in seconds)
11589     * @return {Roo.Element} The Element
11590     */
11591     pause : function(seconds){
11592         var el = this.getFxEl();
11593         var o = {};
11594
11595         el.queueFx(o, function(){
11596             setTimeout(function(){
11597                 el.afterFx(o);
11598             }, seconds * 1000);
11599         });
11600         return this;
11601     },
11602
11603    /**
11604     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
11605     * using the "endOpacity" config option.
11606     * Usage:
11607 <pre><code>
11608 // default: fade in from opacity 0 to 100%
11609 el.fadeIn();
11610
11611 // custom: fade in from opacity 0 to 75% over 2 seconds
11612 el.fadeIn({ endOpacity: .75, duration: 2});
11613
11614 // common config options shown with default values
11615 el.fadeIn({
11616     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11617     easing: 'easeOut',
11618     duration: .5
11619 });
11620 </code></pre>
11621     * @param {Object} options (optional) Object literal with any of the Fx config options
11622     * @return {Roo.Element} The Element
11623     */
11624     fadeIn : function(o){
11625         var el = this.getFxEl();
11626         o = o || {};
11627         el.queueFx(o, function(){
11628             this.setOpacity(0);
11629             this.fixDisplay();
11630             this.dom.style.visibility = 'visible';
11631             var to = o.endOpacity || 1;
11632             arguments.callee.anim = this.fxanim({opacity:{to:to}},
11633                 o, null, .5, "easeOut", function(){
11634                 if(to == 1){
11635                     this.clearOpacity();
11636                 }
11637                 el.afterFx(o);
11638             });
11639         });
11640         return this;
11641     },
11642
11643    /**
11644     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
11645     * using the "endOpacity" config option.
11646     * Usage:
11647 <pre><code>
11648 // default: fade out from the element's current opacity to 0
11649 el.fadeOut();
11650
11651 // custom: fade out from the element's current opacity to 25% over 2 seconds
11652 el.fadeOut({ endOpacity: .25, duration: 2});
11653
11654 // common config options shown with default values
11655 el.fadeOut({
11656     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11657     easing: 'easeOut',
11658     duration: .5
11659     remove: false,
11660     useDisplay: false
11661 });
11662 </code></pre>
11663     * @param {Object} options (optional) Object literal with any of the Fx config options
11664     * @return {Roo.Element} The Element
11665     */
11666     fadeOut : function(o){
11667         var el = this.getFxEl();
11668         o = o || {};
11669         el.queueFx(o, function(){
11670             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11671                 o, null, .5, "easeOut", function(){
11672                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11673                      this.dom.style.display = "none";
11674                 }else{
11675                      this.dom.style.visibility = "hidden";
11676                 }
11677                 this.clearOpacity();
11678                 el.afterFx(o);
11679             });
11680         });
11681         return this;
11682     },
11683
11684    /**
11685     * Animates the transition of an element's dimensions from a starting height/width
11686     * to an ending height/width.
11687     * Usage:
11688 <pre><code>
11689 // change height and width to 100x100 pixels
11690 el.scale(100, 100);
11691
11692 // common config options shown with default values.  The height and width will default to
11693 // the element's existing values if passed as null.
11694 el.scale(
11695     [element's width],
11696     [element's height], {
11697     easing: 'easeOut',
11698     duration: .35
11699 });
11700 </code></pre>
11701     * @param {Number} width  The new width (pass undefined to keep the original width)
11702     * @param {Number} height  The new height (pass undefined to keep the original height)
11703     * @param {Object} options (optional) Object literal with any of the Fx config options
11704     * @return {Roo.Element} The Element
11705     */
11706     scale : function(w, h, o){
11707         this.shift(Roo.apply({}, o, {
11708             width: w,
11709             height: h
11710         }));
11711         return this;
11712     },
11713
11714    /**
11715     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
11716     * Any of these properties not specified in the config object will not be changed.  This effect 
11717     * requires that at least one new dimension, position or opacity setting must be passed in on
11718     * the config object in order for the function to have any effect.
11719     * Usage:
11720 <pre><code>
11721 // slide the element horizontally to x position 200 while changing the height and opacity
11722 el.shift({ x: 200, height: 50, opacity: .8 });
11723
11724 // common config options shown with default values.
11725 el.shift({
11726     width: [element's width],
11727     height: [element's height],
11728     x: [element's x position],
11729     y: [element's y position],
11730     opacity: [element's opacity],
11731     easing: 'easeOut',
11732     duration: .35
11733 });
11734 </code></pre>
11735     * @param {Object} options  Object literal with any of the Fx config options
11736     * @return {Roo.Element} The Element
11737     */
11738     shift : function(o){
11739         var el = this.getFxEl();
11740         o = o || {};
11741         el.queueFx(o, function(){
11742             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
11743             if(w !== undefined){
11744                 a.width = {to: this.adjustWidth(w)};
11745             }
11746             if(h !== undefined){
11747                 a.height = {to: this.adjustHeight(h)};
11748             }
11749             if(x !== undefined || y !== undefined){
11750                 a.points = {to: [
11751                     x !== undefined ? x : this.getX(),
11752                     y !== undefined ? y : this.getY()
11753                 ]};
11754             }
11755             if(op !== undefined){
11756                 a.opacity = {to: op};
11757             }
11758             if(o.xy !== undefined){
11759                 a.points = {to: o.xy};
11760             }
11761             arguments.callee.anim = this.fxanim(a,
11762                 o, 'motion', .35, "easeOut", function(){
11763                 el.afterFx(o);
11764             });
11765         });
11766         return this;
11767     },
11768
11769         /**
11770          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
11771          * ending point of the effect.
11772          * Usage:
11773          *<pre><code>
11774 // default: slide the element downward while fading out
11775 el.ghost();
11776
11777 // custom: slide the element out to the right with a 2-second duration
11778 el.ghost('r', { duration: 2 });
11779
11780 // common config options shown with default values
11781 el.ghost('b', {
11782     easing: 'easeOut',
11783     duration: .5
11784     remove: false,
11785     useDisplay: false
11786 });
11787 </code></pre>
11788          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
11789          * @param {Object} options (optional) Object literal with any of the Fx config options
11790          * @return {Roo.Element} The Element
11791          */
11792     ghost : function(anchor, o){
11793         var el = this.getFxEl();
11794         o = o || {};
11795
11796         el.queueFx(o, function(){
11797             anchor = anchor || "b";
11798
11799             // restore values after effect
11800             var r = this.getFxRestore();
11801             var w = this.getWidth(),
11802                 h = this.getHeight();
11803
11804             var st = this.dom.style;
11805
11806             var after = function(){
11807                 if(o.useDisplay){
11808                     el.setDisplayed(false);
11809                 }else{
11810                     el.hide();
11811                 }
11812
11813                 el.clearOpacity();
11814                 el.setPositioning(r.pos);
11815                 st.width = r.width;
11816                 st.height = r.height;
11817
11818                 el.afterFx(o);
11819             };
11820
11821             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
11822             switch(anchor.toLowerCase()){
11823                 case "t":
11824                     pt.by = [0, -h];
11825                 break;
11826                 case "l":
11827                     pt.by = [-w, 0];
11828                 break;
11829                 case "r":
11830                     pt.by = [w, 0];
11831                 break;
11832                 case "b":
11833                     pt.by = [0, h];
11834                 break;
11835                 case "tl":
11836                     pt.by = [-w, -h];
11837                 break;
11838                 case "bl":
11839                     pt.by = [-w, h];
11840                 break;
11841                 case "br":
11842                     pt.by = [w, h];
11843                 break;
11844                 case "tr":
11845                     pt.by = [w, -h];
11846                 break;
11847             }
11848
11849             arguments.callee.anim = this.fxanim(a,
11850                 o,
11851                 'motion',
11852                 .5,
11853                 "easeOut", after);
11854         });
11855         return this;
11856     },
11857
11858         /**
11859          * Ensures that all effects queued after syncFx is called on the element are
11860          * run concurrently.  This is the opposite of {@link #sequenceFx}.
11861          * @return {Roo.Element} The Element
11862          */
11863     syncFx : function(){
11864         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11865             block : false,
11866             concurrent : true,
11867             stopFx : false
11868         });
11869         return this;
11870     },
11871
11872         /**
11873          * Ensures that all effects queued after sequenceFx is called on the element are
11874          * run in sequence.  This is the opposite of {@link #syncFx}.
11875          * @return {Roo.Element} The Element
11876          */
11877     sequenceFx : function(){
11878         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11879             block : false,
11880             concurrent : false,
11881             stopFx : false
11882         });
11883         return this;
11884     },
11885
11886         /* @private */
11887     nextFx : function(){
11888         var ef = this.fxQueue[0];
11889         if(ef){
11890             ef.call(this);
11891         }
11892     },
11893
11894         /**
11895          * Returns true if the element has any effects actively running or queued, else returns false.
11896          * @return {Boolean} True if element has active effects, else false
11897          */
11898     hasActiveFx : function(){
11899         return this.fxQueue && this.fxQueue[0];
11900     },
11901
11902         /**
11903          * Stops any running effects and clears the element's internal effects queue if it contains
11904          * any additional effects that haven't started yet.
11905          * @return {Roo.Element} The Element
11906          */
11907     stopFx : function(){
11908         if(this.hasActiveFx()){
11909             var cur = this.fxQueue[0];
11910             if(cur && cur.anim && cur.anim.isAnimated()){
11911                 this.fxQueue = [cur]; // clear out others
11912                 cur.anim.stop(true);
11913             }
11914         }
11915         return this;
11916     },
11917
11918         /* @private */
11919     beforeFx : function(o){
11920         if(this.hasActiveFx() && !o.concurrent){
11921            if(o.stopFx){
11922                this.stopFx();
11923                return true;
11924            }
11925            return false;
11926         }
11927         return true;
11928     },
11929
11930         /**
11931          * Returns true if the element is currently blocking so that no other effect can be queued
11932          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11933          * used to ensure that an effect initiated by a user action runs to completion prior to the
11934          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11935          * @return {Boolean} True if blocking, else false
11936          */
11937     hasFxBlock : function(){
11938         var q = this.fxQueue;
11939         return q && q[0] && q[0].block;
11940     },
11941
11942         /* @private */
11943     queueFx : function(o, fn){
11944         if(!this.fxQueue){
11945             this.fxQueue = [];
11946         }
11947         if(!this.hasFxBlock()){
11948             Roo.applyIf(o, this.fxDefaults);
11949             if(!o.concurrent){
11950                 var run = this.beforeFx(o);
11951                 fn.block = o.block;
11952                 this.fxQueue.push(fn);
11953                 if(run){
11954                     this.nextFx();
11955                 }
11956             }else{
11957                 fn.call(this);
11958             }
11959         }
11960         return this;
11961     },
11962
11963         /* @private */
11964     fxWrap : function(pos, o, vis){
11965         var wrap;
11966         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11967             var wrapXY;
11968             if(o.fixPosition){
11969                 wrapXY = this.getXY();
11970             }
11971             var div = document.createElement("div");
11972             div.style.visibility = vis;
11973             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11974             wrap.setPositioning(pos);
11975             if(wrap.getStyle("position") == "static"){
11976                 wrap.position("relative");
11977             }
11978             this.clearPositioning('auto');
11979             wrap.clip();
11980             wrap.dom.appendChild(this.dom);
11981             if(wrapXY){
11982                 wrap.setXY(wrapXY);
11983             }
11984         }
11985         return wrap;
11986     },
11987
11988         /* @private */
11989     fxUnwrap : function(wrap, pos, o){
11990         this.clearPositioning();
11991         this.setPositioning(pos);
11992         if(!o.wrap){
11993             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11994             wrap.remove();
11995         }
11996     },
11997
11998         /* @private */
11999     getFxRestore : function(){
12000         var st = this.dom.style;
12001         return {pos: this.getPositioning(), width: st.width, height : st.height};
12002     },
12003
12004         /* @private */
12005     afterFx : function(o){
12006         if(o.afterStyle){
12007             this.applyStyles(o.afterStyle);
12008         }
12009         if(o.afterCls){
12010             this.addClass(o.afterCls);
12011         }
12012         if(o.remove === true){
12013             this.remove();
12014         }
12015         Roo.callback(o.callback, o.scope, [this]);
12016         if(!o.concurrent){
12017             this.fxQueue.shift();
12018             this.nextFx();
12019         }
12020     },
12021
12022         /* @private */
12023     getFxEl : function(){ // support for composite element fx
12024         return Roo.get(this.dom);
12025     },
12026
12027         /* @private */
12028     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12029         animType = animType || 'run';
12030         opt = opt || {};
12031         var anim = Roo.lib.Anim[animType](
12032             this.dom, args,
12033             (opt.duration || defaultDur) || .35,
12034             (opt.easing || defaultEase) || 'easeOut',
12035             function(){
12036                 Roo.callback(cb, this);
12037             },
12038             this
12039         );
12040         opt.anim = anim;
12041         return anim;
12042     }
12043 };
12044
12045 // backwords compat
12046 Roo.Fx.resize = Roo.Fx.scale;
12047
12048 //When included, Roo.Fx is automatically applied to Element so that all basic
12049 //effects are available directly via the Element API
12050 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12051  * Based on:
12052  * Ext JS Library 1.1.1
12053  * Copyright(c) 2006-2007, Ext JS, LLC.
12054  *
12055  * Originally Released Under LGPL - original licence link has changed is not relivant.
12056  *
12057  * Fork - LGPL
12058  * <script type="text/javascript">
12059  */
12060
12061
12062 /**
12063  * @class Roo.CompositeElement
12064  * Standard composite class. Creates a Roo.Element for every element in the collection.
12065  * <br><br>
12066  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12067  * actions will be performed on all the elements in this collection.</b>
12068  * <br><br>
12069  * All methods return <i>this</i> and can be chained.
12070  <pre><code>
12071  var els = Roo.select("#some-el div.some-class", true);
12072  // or select directly from an existing element
12073  var el = Roo.get('some-el');
12074  el.select('div.some-class', true);
12075
12076  els.setWidth(100); // all elements become 100 width
12077  els.hide(true); // all elements fade out and hide
12078  // or
12079  els.setWidth(100).hide(true);
12080  </code></pre>
12081  */
12082 Roo.CompositeElement = function(els){
12083     this.elements = [];
12084     this.addElements(els);
12085 };
12086 Roo.CompositeElement.prototype = {
12087     isComposite: true,
12088     addElements : function(els){
12089         if(!els) {
12090             return this;
12091         }
12092         if(typeof els == "string"){
12093             els = Roo.Element.selectorFunction(els);
12094         }
12095         var yels = this.elements;
12096         var index = yels.length-1;
12097         for(var i = 0, len = els.length; i < len; i++) {
12098                 yels[++index] = Roo.get(els[i]);
12099         }
12100         return this;
12101     },
12102
12103     /**
12104     * Clears this composite and adds the elements returned by the passed selector.
12105     * @param {String/Array} els A string CSS selector, an array of elements or an element
12106     * @return {CompositeElement} this
12107     */
12108     fill : function(els){
12109         this.elements = [];
12110         this.add(els);
12111         return this;
12112     },
12113
12114     /**
12115     * Filters this composite to only elements that match the passed selector.
12116     * @param {String} selector A string CSS selector
12117     * @param {Boolean} inverse return inverse filter (not matches)
12118     * @return {CompositeElement} this
12119     */
12120     filter : function(selector, inverse){
12121         var els = [];
12122         inverse = inverse || false;
12123         this.each(function(el){
12124             var match = inverse ? !el.is(selector) : el.is(selector);
12125             if(match){
12126                 els[els.length] = el.dom;
12127             }
12128         });
12129         this.fill(els);
12130         return this;
12131     },
12132
12133     invoke : function(fn, args){
12134         var els = this.elements;
12135         for(var i = 0, len = els.length; i < len; i++) {
12136                 Roo.Element.prototype[fn].apply(els[i], args);
12137         }
12138         return this;
12139     },
12140     /**
12141     * Adds elements to this composite.
12142     * @param {String/Array} els A string CSS selector, an array of elements or an element
12143     * @return {CompositeElement} this
12144     */
12145     add : function(els){
12146         if(typeof els == "string"){
12147             this.addElements(Roo.Element.selectorFunction(els));
12148         }else if(els.length !== undefined){
12149             this.addElements(els);
12150         }else{
12151             this.addElements([els]);
12152         }
12153         return this;
12154     },
12155     /**
12156     * Calls the passed function passing (el, this, index) for each element in this composite.
12157     * @param {Function} fn The function to call
12158     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12159     * @return {CompositeElement} this
12160     */
12161     each : function(fn, scope){
12162         var els = this.elements;
12163         for(var i = 0, len = els.length; i < len; i++){
12164             if(fn.call(scope || els[i], els[i], this, i) === false) {
12165                 break;
12166             }
12167         }
12168         return this;
12169     },
12170
12171     /**
12172      * Returns the Element object at the specified index
12173      * @param {Number} index
12174      * @return {Roo.Element}
12175      */
12176     item : function(index){
12177         return this.elements[index] || null;
12178     },
12179
12180     /**
12181      * Returns the first Element
12182      * @return {Roo.Element}
12183      */
12184     first : function(){
12185         return this.item(0);
12186     },
12187
12188     /**
12189      * Returns the last Element
12190      * @return {Roo.Element}
12191      */
12192     last : function(){
12193         return this.item(this.elements.length-1);
12194     },
12195
12196     /**
12197      * Returns the number of elements in this composite
12198      * @return Number
12199      */
12200     getCount : function(){
12201         return this.elements.length;
12202     },
12203
12204     /**
12205      * Returns true if this composite contains the passed element
12206      * @return Boolean
12207      */
12208     contains : function(el){
12209         return this.indexOf(el) !== -1;
12210     },
12211
12212     /**
12213      * Returns true if this composite contains the passed element
12214      * @return Boolean
12215      */
12216     indexOf : function(el){
12217         return this.elements.indexOf(Roo.get(el));
12218     },
12219
12220
12221     /**
12222     * Removes the specified element(s).
12223     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12224     * or an array of any of those.
12225     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12226     * @return {CompositeElement} this
12227     */
12228     removeElement : function(el, removeDom){
12229         if(el instanceof Array){
12230             for(var i = 0, len = el.length; i < len; i++){
12231                 this.removeElement(el[i]);
12232             }
12233             return this;
12234         }
12235         var index = typeof el == 'number' ? el : this.indexOf(el);
12236         if(index !== -1){
12237             if(removeDom){
12238                 var d = this.elements[index];
12239                 if(d.dom){
12240                     d.remove();
12241                 }else{
12242                     d.parentNode.removeChild(d);
12243                 }
12244             }
12245             this.elements.splice(index, 1);
12246         }
12247         return this;
12248     },
12249
12250     /**
12251     * Replaces the specified element with the passed element.
12252     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12253     * to replace.
12254     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12255     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12256     * @return {CompositeElement} this
12257     */
12258     replaceElement : function(el, replacement, domReplace){
12259         var index = typeof el == 'number' ? el : this.indexOf(el);
12260         if(index !== -1){
12261             if(domReplace){
12262                 this.elements[index].replaceWith(replacement);
12263             }else{
12264                 this.elements.splice(index, 1, Roo.get(replacement))
12265             }
12266         }
12267         return this;
12268     },
12269
12270     /**
12271      * Removes all elements.
12272      */
12273     clear : function(){
12274         this.elements = [];
12275     }
12276 };
12277 (function(){
12278     Roo.CompositeElement.createCall = function(proto, fnName){
12279         if(!proto[fnName]){
12280             proto[fnName] = function(){
12281                 return this.invoke(fnName, arguments);
12282             };
12283         }
12284     };
12285     for(var fnName in Roo.Element.prototype){
12286         if(typeof Roo.Element.prototype[fnName] == "function"){
12287             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12288         }
12289     };
12290 })();
12291 /*
12292  * Based on:
12293  * Ext JS Library 1.1.1
12294  * Copyright(c) 2006-2007, Ext JS, LLC.
12295  *
12296  * Originally Released Under LGPL - original licence link has changed is not relivant.
12297  *
12298  * Fork - LGPL
12299  * <script type="text/javascript">
12300  */
12301
12302 /**
12303  * @class Roo.CompositeElementLite
12304  * @extends Roo.CompositeElement
12305  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12306  <pre><code>
12307  var els = Roo.select("#some-el div.some-class");
12308  // or select directly from an existing element
12309  var el = Roo.get('some-el');
12310  el.select('div.some-class');
12311
12312  els.setWidth(100); // all elements become 100 width
12313  els.hide(true); // all elements fade out and hide
12314  // or
12315  els.setWidth(100).hide(true);
12316  </code></pre><br><br>
12317  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12318  * actions will be performed on all the elements in this collection.</b>
12319  */
12320 Roo.CompositeElementLite = function(els){
12321     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12322     this.el = new Roo.Element.Flyweight();
12323 };
12324 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12325     addElements : function(els){
12326         if(els){
12327             if(els instanceof Array){
12328                 this.elements = this.elements.concat(els);
12329             }else{
12330                 var yels = this.elements;
12331                 var index = yels.length-1;
12332                 for(var i = 0, len = els.length; i < len; i++) {
12333                     yels[++index] = els[i];
12334                 }
12335             }
12336         }
12337         return this;
12338     },
12339     invoke : function(fn, args){
12340         var els = this.elements;
12341         var el = this.el;
12342         for(var i = 0, len = els.length; i < len; i++) {
12343             el.dom = els[i];
12344                 Roo.Element.prototype[fn].apply(el, args);
12345         }
12346         return this;
12347     },
12348     /**
12349      * Returns a flyweight Element of the dom element object at the specified index
12350      * @param {Number} index
12351      * @return {Roo.Element}
12352      */
12353     item : function(index){
12354         if(!this.elements[index]){
12355             return null;
12356         }
12357         this.el.dom = this.elements[index];
12358         return this.el;
12359     },
12360
12361     // fixes scope with flyweight
12362     addListener : function(eventName, handler, scope, opt){
12363         var els = this.elements;
12364         for(var i = 0, len = els.length; i < len; i++) {
12365             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12366         }
12367         return this;
12368     },
12369
12370     /**
12371     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12372     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12373     * a reference to the dom node, use el.dom.</b>
12374     * @param {Function} fn The function to call
12375     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12376     * @return {CompositeElement} this
12377     */
12378     each : function(fn, scope){
12379         var els = this.elements;
12380         var el = this.el;
12381         for(var i = 0, len = els.length; i < len; i++){
12382             el.dom = els[i];
12383                 if(fn.call(scope || el, el, this, i) === false){
12384                 break;
12385             }
12386         }
12387         return this;
12388     },
12389
12390     indexOf : function(el){
12391         return this.elements.indexOf(Roo.getDom(el));
12392     },
12393
12394     replaceElement : function(el, replacement, domReplace){
12395         var index = typeof el == 'number' ? el : this.indexOf(el);
12396         if(index !== -1){
12397             replacement = Roo.getDom(replacement);
12398             if(domReplace){
12399                 var d = this.elements[index];
12400                 d.parentNode.insertBefore(replacement, d);
12401                 d.parentNode.removeChild(d);
12402             }
12403             this.elements.splice(index, 1, replacement);
12404         }
12405         return this;
12406     }
12407 });
12408 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12409
12410 /*
12411  * Based on:
12412  * Ext JS Library 1.1.1
12413  * Copyright(c) 2006-2007, Ext JS, LLC.
12414  *
12415  * Originally Released Under LGPL - original licence link has changed is not relivant.
12416  *
12417  * Fork - LGPL
12418  * <script type="text/javascript">
12419  */
12420
12421  
12422
12423 /**
12424  * @class Roo.data.Connection
12425  * @extends Roo.util.Observable
12426  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12427  * either to a configured URL, or to a URL specified at request time. 
12428  * 
12429  * Requests made by this class are asynchronous, and will return immediately. No data from
12430  * the server will be available to the statement immediately following the {@link #request} call.
12431  * To process returned data, use a callback in the request options object, or an event listener.
12432  * 
12433  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12434  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12435  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12436  * property and, if present, the IFRAME's XML document as the responseXML property.
12437  * 
12438  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12439  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12440  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12441  * standard DOM methods.
12442  * @constructor
12443  * @param {Object} config a configuration object.
12444  */
12445 Roo.data.Connection = function(config){
12446     Roo.apply(this, config);
12447     this.addEvents({
12448         /**
12449          * @event beforerequest
12450          * Fires before a network request is made to retrieve a data object.
12451          * @param {Connection} conn This Connection object.
12452          * @param {Object} options The options config object passed to the {@link #request} method.
12453          */
12454         "beforerequest" : true,
12455         /**
12456          * @event requestcomplete
12457          * Fires if the request was successfully completed.
12458          * @param {Connection} conn This Connection object.
12459          * @param {Object} response The XHR object containing the response data.
12460          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12461          * @param {Object} options The options config object passed to the {@link #request} method.
12462          */
12463         "requestcomplete" : true,
12464         /**
12465          * @event requestexception
12466          * Fires if an error HTTP status was returned from the server.
12467          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12468          * @param {Connection} conn This Connection object.
12469          * @param {Object} response The XHR object containing the response data.
12470          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12471          * @param {Object} options The options config object passed to the {@link #request} method.
12472          */
12473         "requestexception" : true
12474     });
12475     Roo.data.Connection.superclass.constructor.call(this);
12476 };
12477
12478 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12479     /**
12480      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12481      */
12482     /**
12483      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12484      * extra parameters to each request made by this object. (defaults to undefined)
12485      */
12486     /**
12487      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12488      *  to each request made by this object. (defaults to undefined)
12489      */
12490     /**
12491      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
12492      */
12493     /**
12494      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12495      */
12496     timeout : 30000,
12497     /**
12498      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12499      * @type Boolean
12500      */
12501     autoAbort:false,
12502
12503     /**
12504      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12505      * @type Boolean
12506      */
12507     disableCaching: true,
12508
12509     /**
12510      * Sends an HTTP request to a remote server.
12511      * @param {Object} options An object which may contain the following properties:<ul>
12512      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12513      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12514      * request, a url encoded string or a function to call to get either.</li>
12515      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12516      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12517      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12518      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12519      * <li>options {Object} The parameter to the request call.</li>
12520      * <li>success {Boolean} True if the request succeeded.</li>
12521      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12522      * </ul></li>
12523      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12524      * The callback is passed the following parameters:<ul>
12525      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12526      * <li>options {Object} The parameter to the request call.</li>
12527      * </ul></li>
12528      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12529      * The callback is passed the following parameters:<ul>
12530      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12531      * <li>options {Object} The parameter to the request call.</li>
12532      * </ul></li>
12533      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12534      * for the callback function. Defaults to the browser window.</li>
12535      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12536      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12537      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12538      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12539      * params for the post data. Any params will be appended to the URL.</li>
12540      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12541      * </ul>
12542      * @return {Number} transactionId
12543      */
12544     request : function(o){
12545         if(this.fireEvent("beforerequest", this, o) !== false){
12546             var p = o.params;
12547
12548             if(typeof p == "function"){
12549                 p = p.call(o.scope||window, o);
12550             }
12551             if(typeof p == "object"){
12552                 p = Roo.urlEncode(o.params);
12553             }
12554             if(this.extraParams){
12555                 var extras = Roo.urlEncode(this.extraParams);
12556                 p = p ? (p + '&' + extras) : extras;
12557             }
12558
12559             var url = o.url || this.url;
12560             if(typeof url == 'function'){
12561                 url = url.call(o.scope||window, o);
12562             }
12563
12564             if(o.form){
12565                 var form = Roo.getDom(o.form);
12566                 url = url || form.action;
12567
12568                 var enctype = form.getAttribute("enctype");
12569                 
12570                 if (o.formData) {
12571                     return this.doFormDataUpload(o, url);
12572                 }
12573                 
12574                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12575                     return this.doFormUpload(o, p, url);
12576                 }
12577                 var f = Roo.lib.Ajax.serializeForm(form);
12578                 p = p ? (p + '&' + f) : f;
12579             }
12580             
12581             if (!o.form && o.formData) {
12582                 o.formData = o.formData === true ? new FormData() : o.formData;
12583                 for (var k in o.params) {
12584                     o.formData.append(k,o.params[k]);
12585                 }
12586                     
12587                 return this.doFormDataUpload(o, url);
12588             }
12589             
12590
12591             var hs = o.headers;
12592             if(this.defaultHeaders){
12593                 hs = Roo.apply(hs || {}, this.defaultHeaders);
12594                 if(!o.headers){
12595                     o.headers = hs;
12596                 }
12597             }
12598
12599             var cb = {
12600                 success: this.handleResponse,
12601                 failure: this.handleFailure,
12602                 scope: this,
12603                 argument: {options: o},
12604                 timeout : o.timeout || this.timeout
12605             };
12606
12607             var method = o.method||this.method||(p ? "POST" : "GET");
12608
12609             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12610                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12611             }
12612
12613             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12614                 if(o.autoAbort){
12615                     this.abort();
12616                 }
12617             }else if(this.autoAbort !== false){
12618                 this.abort();
12619             }
12620
12621             if((method == 'GET' && p) || o.xmlData){
12622                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12623                 p = '';
12624             }
12625             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12626             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12627             Roo.lib.Ajax.useDefaultHeader == true;
12628             return this.transId;
12629         }else{
12630             Roo.callback(o.callback, o.scope, [o, null, null]);
12631             return null;
12632         }
12633     },
12634
12635     /**
12636      * Determine whether this object has a request outstanding.
12637      * @param {Number} transactionId (Optional) defaults to the last transaction
12638      * @return {Boolean} True if there is an outstanding request.
12639      */
12640     isLoading : function(transId){
12641         if(transId){
12642             return Roo.lib.Ajax.isCallInProgress(transId);
12643         }else{
12644             return this.transId ? true : false;
12645         }
12646     },
12647
12648     /**
12649      * Aborts any outstanding request.
12650      * @param {Number} transactionId (Optional) defaults to the last transaction
12651      */
12652     abort : function(transId){
12653         if(transId || this.isLoading()){
12654             Roo.lib.Ajax.abort(transId || this.transId);
12655         }
12656     },
12657
12658     // private
12659     handleResponse : function(response){
12660         this.transId = false;
12661         var options = response.argument.options;
12662         response.argument = options ? options.argument : null;
12663         this.fireEvent("requestcomplete", this, response, options);
12664         Roo.callback(options.success, options.scope, [response, options]);
12665         Roo.callback(options.callback, options.scope, [options, true, response]);
12666     },
12667
12668     // private
12669     handleFailure : function(response, e){
12670         this.transId = false;
12671         var options = response.argument.options;
12672         response.argument = options ? options.argument : null;
12673         this.fireEvent("requestexception", this, response, options, e);
12674         Roo.callback(options.failure, options.scope, [response, options]);
12675         Roo.callback(options.callback, options.scope, [options, false, response]);
12676     },
12677
12678     // private
12679     doFormUpload : function(o, ps, url){
12680         var id = Roo.id();
12681         var frame = document.createElement('iframe');
12682         frame.id = id;
12683         frame.name = id;
12684         frame.className = 'x-hidden';
12685         if(Roo.isIE){
12686             frame.src = Roo.SSL_SECURE_URL;
12687         }
12688         document.body.appendChild(frame);
12689
12690         if(Roo.isIE){
12691            document.frames[id].name = id;
12692         }
12693
12694         var form = Roo.getDom(o.form);
12695         form.target = id;
12696         form.method = 'POST';
12697         form.enctype = form.encoding = 'multipart/form-data';
12698         if(url){
12699             form.action = url;
12700         }
12701
12702         var hiddens, hd;
12703         if(ps){ // add dynamic params
12704             hiddens = [];
12705             ps = Roo.urlDecode(ps, false);
12706             for(var k in ps){
12707                 if(ps.hasOwnProperty(k)){
12708                     hd = document.createElement('input');
12709                     hd.type = 'hidden';
12710                     hd.name = k;
12711                     hd.value = ps[k];
12712                     form.appendChild(hd);
12713                     hiddens.push(hd);
12714                 }
12715             }
12716         }
12717
12718         function cb(){
12719             var r = {  // bogus response object
12720                 responseText : '',
12721                 responseXML : null
12722             };
12723
12724             r.argument = o ? o.argument : null;
12725
12726             try { //
12727                 var doc;
12728                 if(Roo.isIE){
12729                     doc = frame.contentWindow.document;
12730                 }else {
12731                     doc = (frame.contentDocument || window.frames[id].document);
12732                 }
12733                 if(doc && doc.body){
12734                     r.responseText = doc.body.innerHTML;
12735                 }
12736                 if(doc && doc.XMLDocument){
12737                     r.responseXML = doc.XMLDocument;
12738                 }else {
12739                     r.responseXML = doc;
12740                 }
12741             }
12742             catch(e) {
12743                 // ignore
12744             }
12745
12746             Roo.EventManager.removeListener(frame, 'load', cb, this);
12747
12748             this.fireEvent("requestcomplete", this, r, o);
12749             Roo.callback(o.success, o.scope, [r, o]);
12750             Roo.callback(o.callback, o.scope, [o, true, r]);
12751
12752             setTimeout(function(){document.body.removeChild(frame);}, 100);
12753         }
12754
12755         Roo.EventManager.on(frame, 'load', cb, this);
12756         form.submit();
12757
12758         if(hiddens){ // remove dynamic params
12759             for(var i = 0, len = hiddens.length; i < len; i++){
12760                 form.removeChild(hiddens[i]);
12761             }
12762         }
12763     },
12764     // this is a 'formdata version???'
12765     
12766     
12767     doFormDataUpload : function(o,  url)
12768     {
12769         var formData;
12770         if (o.form) {
12771             var form =  Roo.getDom(o.form);
12772             form.enctype = form.encoding = 'multipart/form-data';
12773             formData = o.formData === true ? new FormData(form) : o.formData;
12774         } else {
12775             formData = o.formData === true ? new FormData() : o.formData;
12776         }
12777         
12778       
12779         var cb = {
12780             success: this.handleResponse,
12781             failure: this.handleFailure,
12782             scope: this,
12783             argument: {options: o},
12784             timeout : o.timeout || this.timeout
12785         };
12786  
12787         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12788             if(o.autoAbort){
12789                 this.abort();
12790             }
12791         }else if(this.autoAbort !== false){
12792             this.abort();
12793         }
12794
12795         //Roo.lib.Ajax.defaultPostHeader = null;
12796         Roo.lib.Ajax.useDefaultHeader = false;
12797         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
12798         Roo.lib.Ajax.useDefaultHeader = true;
12799  
12800          
12801     }
12802     
12803 });
12804 /*
12805  * Based on:
12806  * Ext JS Library 1.1.1
12807  * Copyright(c) 2006-2007, Ext JS, LLC.
12808  *
12809  * Originally Released Under LGPL - original licence link has changed is not relivant.
12810  *
12811  * Fork - LGPL
12812  * <script type="text/javascript">
12813  */
12814  
12815 /**
12816  * Global Ajax request class.
12817  * 
12818  * @class Roo.Ajax
12819  * @extends Roo.data.Connection
12820  * @static
12821  * 
12822  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
12823  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
12824  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
12825  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
12826  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12827  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
12828  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
12829  */
12830 Roo.Ajax = new Roo.data.Connection({
12831     // fix up the docs
12832     /**
12833      * @scope Roo.Ajax
12834      * @type {Boolear} 
12835      */
12836     autoAbort : false,
12837
12838     /**
12839      * Serialize the passed form into a url encoded string
12840      * @scope Roo.Ajax
12841      * @param {String/HTMLElement} form
12842      * @return {String}
12843      */
12844     serializeForm : function(form){
12845         return Roo.lib.Ajax.serializeForm(form);
12846     }
12847 });/*
12848  * Based on:
12849  * Ext JS Library 1.1.1
12850  * Copyright(c) 2006-2007, Ext JS, LLC.
12851  *
12852  * Originally Released Under LGPL - original licence link has changed is not relivant.
12853  *
12854  * Fork - LGPL
12855  * <script type="text/javascript">
12856  */
12857
12858  
12859 /**
12860  * @class Roo.UpdateManager
12861  * @extends Roo.util.Observable
12862  * Provides AJAX-style update for Element object.<br><br>
12863  * Usage:<br>
12864  * <pre><code>
12865  * // Get it from a Roo.Element object
12866  * var el = Roo.get("foo");
12867  * var mgr = el.getUpdateManager();
12868  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12869  * ...
12870  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12871  * <br>
12872  * // or directly (returns the same UpdateManager instance)
12873  * var mgr = new Roo.UpdateManager("myElementId");
12874  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12875  * mgr.on("update", myFcnNeedsToKnow);
12876  * <br>
12877    // short handed call directly from the element object
12878    Roo.get("foo").load({
12879         url: "bar.php",
12880         scripts:true,
12881         params: "for=bar",
12882         text: "Loading Foo..."
12883    });
12884  * </code></pre>
12885  * @constructor
12886  * Create new UpdateManager directly.
12887  * @param {String/HTMLElement/Roo.Element} el The element to update
12888  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
12889  */
12890 Roo.UpdateManager = function(el, forceNew){
12891     el = Roo.get(el);
12892     if(!forceNew && el.updateManager){
12893         return el.updateManager;
12894     }
12895     /**
12896      * The Element object
12897      * @type Roo.Element
12898      */
12899     this.el = el;
12900     /**
12901      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12902      * @type String
12903      */
12904     this.defaultUrl = null;
12905
12906     this.addEvents({
12907         /**
12908          * @event beforeupdate
12909          * Fired before an update is made, return false from your handler and the update is cancelled.
12910          * @param {Roo.Element} el
12911          * @param {String/Object/Function} url
12912          * @param {String/Object} params
12913          */
12914         "beforeupdate": true,
12915         /**
12916          * @event update
12917          * Fired after successful update is made.
12918          * @param {Roo.Element} el
12919          * @param {Object} oResponseObject The response Object
12920          */
12921         "update": true,
12922         /**
12923          * @event failure
12924          * Fired on update failure.
12925          * @param {Roo.Element} el
12926          * @param {Object} oResponseObject The response Object
12927          */
12928         "failure": true
12929     });
12930     var d = Roo.UpdateManager.defaults;
12931     /**
12932      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12933      * @type String
12934      */
12935     this.sslBlankUrl = d.sslBlankUrl;
12936     /**
12937      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12938      * @type Boolean
12939      */
12940     this.disableCaching = d.disableCaching;
12941     /**
12942      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12943      * @type String
12944      */
12945     this.indicatorText = d.indicatorText;
12946     /**
12947      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12948      * @type String
12949      */
12950     this.showLoadIndicator = d.showLoadIndicator;
12951     /**
12952      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12953      * @type Number
12954      */
12955     this.timeout = d.timeout;
12956
12957     /**
12958      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12959      * @type Boolean
12960      */
12961     this.loadScripts = d.loadScripts;
12962
12963     /**
12964      * Transaction object of current executing transaction
12965      */
12966     this.transaction = null;
12967
12968     /**
12969      * @private
12970      */
12971     this.autoRefreshProcId = null;
12972     /**
12973      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12974      * @type Function
12975      */
12976     this.refreshDelegate = this.refresh.createDelegate(this);
12977     /**
12978      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12979      * @type Function
12980      */
12981     this.updateDelegate = this.update.createDelegate(this);
12982     /**
12983      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12984      * @type Function
12985      */
12986     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12987     /**
12988      * @private
12989      */
12990     this.successDelegate = this.processSuccess.createDelegate(this);
12991     /**
12992      * @private
12993      */
12994     this.failureDelegate = this.processFailure.createDelegate(this);
12995
12996     if(!this.renderer){
12997      /**
12998       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12999       */
13000     this.renderer = new Roo.UpdateManager.BasicRenderer();
13001     }
13002     
13003     Roo.UpdateManager.superclass.constructor.call(this);
13004 };
13005
13006 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13007     /**
13008      * Get the Element this UpdateManager is bound to
13009      * @return {Roo.Element} The element
13010      */
13011     getEl : function(){
13012         return this.el;
13013     },
13014     /**
13015      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13016      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
13017 <pre><code>
13018 um.update({<br/>
13019     url: "your-url.php",<br/>
13020     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13021     callback: yourFunction,<br/>
13022     scope: yourObject, //(optional scope)  <br/>
13023     discardUrl: false, <br/>
13024     nocache: false,<br/>
13025     text: "Loading...",<br/>
13026     timeout: 30,<br/>
13027     scripts: false<br/>
13028 });
13029 </code></pre>
13030      * The only required property is url. The optional properties nocache, text and scripts
13031      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13032      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
13033      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13034      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
13035      */
13036     update : function(url, params, callback, discardUrl){
13037         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13038             var method = this.method,
13039                 cfg;
13040             if(typeof url == "object"){ // must be config object
13041                 cfg = url;
13042                 url = cfg.url;
13043                 params = params || cfg.params;
13044                 callback = callback || cfg.callback;
13045                 discardUrl = discardUrl || cfg.discardUrl;
13046                 if(callback && cfg.scope){
13047                     callback = callback.createDelegate(cfg.scope);
13048                 }
13049                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13050                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13051                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13052                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13053                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13054             }
13055             this.showLoading();
13056             if(!discardUrl){
13057                 this.defaultUrl = url;
13058             }
13059             if(typeof url == "function"){
13060                 url = url.call(this);
13061             }
13062
13063             method = method || (params ? "POST" : "GET");
13064             if(method == "GET"){
13065                 url = this.prepareUrl(url);
13066             }
13067
13068             var o = Roo.apply(cfg ||{}, {
13069                 url : url,
13070                 params: params,
13071                 success: this.successDelegate,
13072                 failure: this.failureDelegate,
13073                 callback: undefined,
13074                 timeout: (this.timeout*1000),
13075                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13076             });
13077             Roo.log("updated manager called with timeout of " + o.timeout);
13078             this.transaction = Roo.Ajax.request(o);
13079         }
13080     },
13081
13082     /**
13083      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
13084      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13085      * @param {String/HTMLElement} form The form Id or form element
13086      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13087      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13088      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13089      */
13090     formUpdate : function(form, url, reset, callback){
13091         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13092             if(typeof url == "function"){
13093                 url = url.call(this);
13094             }
13095             form = Roo.getDom(form);
13096             this.transaction = Roo.Ajax.request({
13097                 form: form,
13098                 url:url,
13099                 success: this.successDelegate,
13100                 failure: this.failureDelegate,
13101                 timeout: (this.timeout*1000),
13102                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13103             });
13104             this.showLoading.defer(1, this);
13105         }
13106     },
13107
13108     /**
13109      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13110      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13111      */
13112     refresh : function(callback){
13113         if(this.defaultUrl == null){
13114             return;
13115         }
13116         this.update(this.defaultUrl, null, callback, true);
13117     },
13118
13119     /**
13120      * Set this element to auto refresh.
13121      * @param {Number} interval How often to update (in seconds).
13122      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
13123      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
13124      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13125      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13126      */
13127     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13128         if(refreshNow){
13129             this.update(url || this.defaultUrl, params, callback, true);
13130         }
13131         if(this.autoRefreshProcId){
13132             clearInterval(this.autoRefreshProcId);
13133         }
13134         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13135     },
13136
13137     /**
13138      * Stop auto refresh on this element.
13139      */
13140      stopAutoRefresh : function(){
13141         if(this.autoRefreshProcId){
13142             clearInterval(this.autoRefreshProcId);
13143             delete this.autoRefreshProcId;
13144         }
13145     },
13146
13147     isAutoRefreshing : function(){
13148        return this.autoRefreshProcId ? true : false;
13149     },
13150     /**
13151      * Called to update the element to "Loading" state. Override to perform custom action.
13152      */
13153     showLoading : function(){
13154         if(this.showLoadIndicator){
13155             this.el.update(this.indicatorText);
13156         }
13157     },
13158
13159     /**
13160      * Adds unique parameter to query string if disableCaching = true
13161      * @private
13162      */
13163     prepareUrl : function(url){
13164         if(this.disableCaching){
13165             var append = "_dc=" + (new Date().getTime());
13166             if(url.indexOf("?") !== -1){
13167                 url += "&" + append;
13168             }else{
13169                 url += "?" + append;
13170             }
13171         }
13172         return url;
13173     },
13174
13175     /**
13176      * @private
13177      */
13178     processSuccess : function(response){
13179         this.transaction = null;
13180         if(response.argument.form && response.argument.reset){
13181             try{ // put in try/catch since some older FF releases had problems with this
13182                 response.argument.form.reset();
13183             }catch(e){}
13184         }
13185         if(this.loadScripts){
13186             this.renderer.render(this.el, response, this,
13187                 this.updateComplete.createDelegate(this, [response]));
13188         }else{
13189             this.renderer.render(this.el, response, this);
13190             this.updateComplete(response);
13191         }
13192     },
13193
13194     updateComplete : function(response){
13195         this.fireEvent("update", this.el, response);
13196         if(typeof response.argument.callback == "function"){
13197             response.argument.callback(this.el, true, response);
13198         }
13199     },
13200
13201     /**
13202      * @private
13203      */
13204     processFailure : function(response){
13205         this.transaction = null;
13206         this.fireEvent("failure", this.el, response);
13207         if(typeof response.argument.callback == "function"){
13208             response.argument.callback(this.el, false, response);
13209         }
13210     },
13211
13212     /**
13213      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13214      * @param {Object} renderer The object implementing the render() method
13215      */
13216     setRenderer : function(renderer){
13217         this.renderer = renderer;
13218     },
13219
13220     getRenderer : function(){
13221        return this.renderer;
13222     },
13223
13224     /**
13225      * Set the defaultUrl used for updates
13226      * @param {String/Function} defaultUrl The url or a function to call to get the url
13227      */
13228     setDefaultUrl : function(defaultUrl){
13229         this.defaultUrl = defaultUrl;
13230     },
13231
13232     /**
13233      * Aborts the executing transaction
13234      */
13235     abort : function(){
13236         if(this.transaction){
13237             Roo.Ajax.abort(this.transaction);
13238         }
13239     },
13240
13241     /**
13242      * Returns true if an update is in progress
13243      * @return {Boolean}
13244      */
13245     isUpdating : function(){
13246         if(this.transaction){
13247             return Roo.Ajax.isLoading(this.transaction);
13248         }
13249         return false;
13250     }
13251 });
13252
13253 /**
13254  * @class Roo.UpdateManager.defaults
13255  * @static (not really - but it helps the doc tool)
13256  * The defaults collection enables customizing the default properties of UpdateManager
13257  */
13258    Roo.UpdateManager.defaults = {
13259        /**
13260          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13261          * @type Number
13262          */
13263          timeout : 30,
13264
13265          /**
13266          * True to process scripts by default (Defaults to false).
13267          * @type Boolean
13268          */
13269         loadScripts : false,
13270
13271         /**
13272         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13273         * @type String
13274         */
13275         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13276         /**
13277          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13278          * @type Boolean
13279          */
13280         disableCaching : false,
13281         /**
13282          * Whether to show indicatorText when loading (Defaults to true).
13283          * @type Boolean
13284          */
13285         showLoadIndicator : true,
13286         /**
13287          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13288          * @type String
13289          */
13290         indicatorText : '<div class="loading-indicator">Loading...</div>'
13291    };
13292
13293 /**
13294  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13295  *Usage:
13296  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13297  * @param {String/HTMLElement/Roo.Element} el The element to update
13298  * @param {String} url The url
13299  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13300  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13301  * @static
13302  * @deprecated
13303  * @member Roo.UpdateManager
13304  */
13305 Roo.UpdateManager.updateElement = function(el, url, params, options){
13306     var um = Roo.get(el, true).getUpdateManager();
13307     Roo.apply(um, options);
13308     um.update(url, params, options ? options.callback : null);
13309 };
13310 // alias for backwards compat
13311 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13312 /**
13313  * @class Roo.UpdateManager.BasicRenderer
13314  * Default Content renderer. Updates the elements innerHTML with the responseText.
13315  */
13316 Roo.UpdateManager.BasicRenderer = function(){};
13317
13318 Roo.UpdateManager.BasicRenderer.prototype = {
13319     /**
13320      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13321      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13322      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13323      * @param {Roo.Element} el The element being rendered
13324      * @param {Object} response The YUI Connect response object
13325      * @param {UpdateManager} updateManager The calling update manager
13326      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13327      */
13328      render : function(el, response, updateManager, callback){
13329         el.update(response.responseText, updateManager.loadScripts, callback);
13330     }
13331 };
13332 /*
13333  * Based on:
13334  * Roo JS
13335  * (c)) Alan Knowles
13336  * Licence : LGPL
13337  */
13338
13339
13340 /**
13341  * @class Roo.DomTemplate
13342  * @extends Roo.Template
13343  * An effort at a dom based template engine..
13344  *
13345  * Similar to XTemplate, except it uses dom parsing to create the template..
13346  *
13347  * Supported features:
13348  *
13349  *  Tags:
13350
13351 <pre><code>
13352       {a_variable} - output encoded.
13353       {a_variable.format:("Y-m-d")} - call a method on the variable
13354       {a_variable:raw} - unencoded output
13355       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13356       {a_variable:this.method_on_template(...)} - call a method on the template object.
13357  
13358 </code></pre>
13359  *  The tpl tag:
13360 <pre><code>
13361         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13362         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13363         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13364         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13365   
13366 </code></pre>
13367  *      
13368  */
13369 Roo.DomTemplate = function()
13370 {
13371      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13372      if (this.html) {
13373         this.compile();
13374      }
13375 };
13376
13377
13378 Roo.extend(Roo.DomTemplate, Roo.Template, {
13379     /**
13380      * id counter for sub templates.
13381      */
13382     id : 0,
13383     /**
13384      * flag to indicate if dom parser is inside a pre,
13385      * it will strip whitespace if not.
13386      */
13387     inPre : false,
13388     
13389     /**
13390      * The various sub templates
13391      */
13392     tpls : false,
13393     
13394     
13395     
13396     /**
13397      *
13398      * basic tag replacing syntax
13399      * WORD:WORD()
13400      *
13401      * // you can fake an object call by doing this
13402      *  x.t:(test,tesT) 
13403      * 
13404      */
13405     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13406     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13407     
13408     iterChild : function (node, method) {
13409         
13410         var oldPre = this.inPre;
13411         if (node.tagName == 'PRE') {
13412             this.inPre = true;
13413         }
13414         for( var i = 0; i < node.childNodes.length; i++) {
13415             method.call(this, node.childNodes[i]);
13416         }
13417         this.inPre = oldPre;
13418     },
13419     
13420     
13421     
13422     /**
13423      * compile the template
13424      *
13425      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13426      *
13427      */
13428     compile: function()
13429     {
13430         var s = this.html;
13431         
13432         // covert the html into DOM...
13433         var doc = false;
13434         var div =false;
13435         try {
13436             doc = document.implementation.createHTMLDocument("");
13437             doc.documentElement.innerHTML =   this.html  ;
13438             div = doc.documentElement;
13439         } catch (e) {
13440             // old IE... - nasty -- it causes all sorts of issues.. with
13441             // images getting pulled from server..
13442             div = document.createElement('div');
13443             div.innerHTML = this.html;
13444         }
13445         //doc.documentElement.innerHTML = htmlBody
13446          
13447         
13448         
13449         this.tpls = [];
13450         var _t = this;
13451         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13452         
13453         var tpls = this.tpls;
13454         
13455         // create a top level template from the snippet..
13456         
13457         //Roo.log(div.innerHTML);
13458         
13459         var tpl = {
13460             uid : 'master',
13461             id : this.id++,
13462             attr : false,
13463             value : false,
13464             body : div.innerHTML,
13465             
13466             forCall : false,
13467             execCall : false,
13468             dom : div,
13469             isTop : true
13470             
13471         };
13472         tpls.unshift(tpl);
13473         
13474         
13475         // compile them...
13476         this.tpls = [];
13477         Roo.each(tpls, function(tp){
13478             this.compileTpl(tp);
13479             this.tpls[tp.id] = tp;
13480         }, this);
13481         
13482         this.master = tpls[0];
13483         return this;
13484         
13485         
13486     },
13487     
13488     compileNode : function(node, istop) {
13489         // test for
13490         //Roo.log(node);
13491         
13492         
13493         // skip anything not a tag..
13494         if (node.nodeType != 1) {
13495             if (node.nodeType == 3 && !this.inPre) {
13496                 // reduce white space..
13497                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13498                 
13499             }
13500             return;
13501         }
13502         
13503         var tpl = {
13504             uid : false,
13505             id : false,
13506             attr : false,
13507             value : false,
13508             body : '',
13509             
13510             forCall : false,
13511             execCall : false,
13512             dom : false,
13513             isTop : istop
13514             
13515             
13516         };
13517         
13518         
13519         switch(true) {
13520             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13521             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13522             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13523             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13524             // no default..
13525         }
13526         
13527         
13528         if (!tpl.attr) {
13529             // just itterate children..
13530             this.iterChild(node,this.compileNode);
13531             return;
13532         }
13533         tpl.uid = this.id++;
13534         tpl.value = node.getAttribute('roo-' +  tpl.attr);
13535         node.removeAttribute('roo-'+ tpl.attr);
13536         if (tpl.attr != 'name') {
13537             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13538             node.parentNode.replaceChild(placeholder,  node);
13539         } else {
13540             
13541             var placeholder =  document.createElement('span');
13542             placeholder.className = 'roo-tpl-' + tpl.value;
13543             node.parentNode.replaceChild(placeholder,  node);
13544         }
13545         
13546         // parent now sees '{domtplXXXX}
13547         this.iterChild(node,this.compileNode);
13548         
13549         // we should now have node body...
13550         var div = document.createElement('div');
13551         div.appendChild(node);
13552         tpl.dom = node;
13553         // this has the unfortunate side effect of converting tagged attributes
13554         // eg. href="{...}" into %7C...%7D
13555         // this has been fixed by searching for those combo's although it's a bit hacky..
13556         
13557         
13558         tpl.body = div.innerHTML;
13559         
13560         
13561          
13562         tpl.id = tpl.uid;
13563         switch(tpl.attr) {
13564             case 'for' :
13565                 switch (tpl.value) {
13566                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13567                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13568                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13569                 }
13570                 break;
13571             
13572             case 'exec':
13573                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13574                 break;
13575             
13576             case 'if':     
13577                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13578                 break;
13579             
13580             case 'name':
13581                 tpl.id  = tpl.value; // replace non characters???
13582                 break;
13583             
13584         }
13585         
13586         
13587         this.tpls.push(tpl);
13588         
13589         
13590         
13591     },
13592     
13593     
13594     
13595     
13596     /**
13597      * Compile a segment of the template into a 'sub-template'
13598      *
13599      * 
13600      * 
13601      *
13602      */
13603     compileTpl : function(tpl)
13604     {
13605         var fm = Roo.util.Format;
13606         var useF = this.disableFormats !== true;
13607         
13608         var sep = Roo.isGecko ? "+\n" : ",\n";
13609         
13610         var undef = function(str) {
13611             Roo.debug && Roo.log("Property not found :"  + str);
13612             return '';
13613         };
13614           
13615         //Roo.log(tpl.body);
13616         
13617         
13618         
13619         var fn = function(m, lbrace, name, format, args)
13620         {
13621             //Roo.log("ARGS");
13622             //Roo.log(arguments);
13623             args = args ? args.replace(/\\'/g,"'") : args;
13624             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13625             if (typeof(format) == 'undefined') {
13626                 format =  'htmlEncode'; 
13627             }
13628             if (format == 'raw' ) {
13629                 format = false;
13630             }
13631             
13632             if(name.substr(0, 6) == 'domtpl'){
13633                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13634             }
13635             
13636             // build an array of options to determine if value is undefined..
13637             
13638             // basically get 'xxxx.yyyy' then do
13639             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13640             //    (function () { Roo.log("Property not found"); return ''; })() :
13641             //    ......
13642             
13643             var udef_ar = [];
13644             var lookfor = '';
13645             Roo.each(name.split('.'), function(st) {
13646                 lookfor += (lookfor.length ? '.': '') + st;
13647                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
13648             });
13649             
13650             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13651             
13652             
13653             if(format && useF){
13654                 
13655                 args = args ? ',' + args : "";
13656                  
13657                 if(format.substr(0, 5) != "this."){
13658                     format = "fm." + format + '(';
13659                 }else{
13660                     format = 'this.call("'+ format.substr(5) + '", ';
13661                     args = ", values";
13662                 }
13663                 
13664                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
13665             }
13666              
13667             if (args && args.length) {
13668                 // called with xxyx.yuu:(test,test)
13669                 // change to ()
13670                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
13671             }
13672             // raw.. - :raw modifier..
13673             return "'"+ sep + udef_st  + name + ")"+sep+"'";
13674             
13675         };
13676         var body;
13677         // branched to use + in gecko and [].join() in others
13678         if(Roo.isGecko){
13679             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
13680                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13681                     "';};};";
13682         }else{
13683             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
13684             body.push(tpl.body.replace(/(\r\n|\n)/g,
13685                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
13686             body.push("'].join('');};};");
13687             body = body.join('');
13688         }
13689         
13690         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
13691        
13692         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
13693         eval(body);
13694         
13695         return this;
13696     },
13697      
13698     /**
13699      * same as applyTemplate, except it's done to one of the subTemplates
13700      * when using named templates, you can do:
13701      *
13702      * var str = pl.applySubTemplate('your-name', values);
13703      *
13704      * 
13705      * @param {Number} id of the template
13706      * @param {Object} values to apply to template
13707      * @param {Object} parent (normaly the instance of this object)
13708      */
13709     applySubTemplate : function(id, values, parent)
13710     {
13711         
13712         
13713         var t = this.tpls[id];
13714         
13715         
13716         try { 
13717             if(t.ifCall && !t.ifCall.call(this, values, parent)){
13718                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
13719                 return '';
13720             }
13721         } catch(e) {
13722             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
13723             Roo.log(values);
13724           
13725             return '';
13726         }
13727         try { 
13728             
13729             if(t.execCall && t.execCall.call(this, values, parent)){
13730                 return '';
13731             }
13732         } catch(e) {
13733             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13734             Roo.log(values);
13735             return '';
13736         }
13737         
13738         try {
13739             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
13740             parent = t.target ? values : parent;
13741             if(t.forCall && vs instanceof Array){
13742                 var buf = [];
13743                 for(var i = 0, len = vs.length; i < len; i++){
13744                     try {
13745                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
13746                     } catch (e) {
13747                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13748                         Roo.log(e.body);
13749                         //Roo.log(t.compiled);
13750                         Roo.log(vs[i]);
13751                     }   
13752                 }
13753                 return buf.join('');
13754             }
13755         } catch (e) {
13756             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13757             Roo.log(values);
13758             return '';
13759         }
13760         try {
13761             return t.compiled.call(this, vs, parent);
13762         } catch (e) {
13763             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13764             Roo.log(e.body);
13765             //Roo.log(t.compiled);
13766             Roo.log(values);
13767             return '';
13768         }
13769     },
13770
13771    
13772
13773     applyTemplate : function(values){
13774         return this.master.compiled.call(this, values, {});
13775         //var s = this.subs;
13776     },
13777
13778     apply : function(){
13779         return this.applyTemplate.apply(this, arguments);
13780     }
13781
13782  });
13783
13784 Roo.DomTemplate.from = function(el){
13785     el = Roo.getDom(el);
13786     return new Roo.Domtemplate(el.value || el.innerHTML);
13787 };/*
13788  * Based on:
13789  * Ext JS Library 1.1.1
13790  * Copyright(c) 2006-2007, Ext JS, LLC.
13791  *
13792  * Originally Released Under LGPL - original licence link has changed is not relivant.
13793  *
13794  * Fork - LGPL
13795  * <script type="text/javascript">
13796  */
13797
13798 /**
13799  * @class Roo.util.DelayedTask
13800  * Provides a convenient method of performing setTimeout where a new
13801  * timeout cancels the old timeout. An example would be performing validation on a keypress.
13802  * You can use this class to buffer
13803  * the keypress events for a certain number of milliseconds, and perform only if they stop
13804  * for that amount of time.
13805  * @constructor The parameters to this constructor serve as defaults and are not required.
13806  * @param {Function} fn (optional) The default function to timeout
13807  * @param {Object} scope (optional) The default scope of that timeout
13808  * @param {Array} args (optional) The default Array of arguments
13809  */
13810 Roo.util.DelayedTask = function(fn, scope, args){
13811     var id = null, d, t;
13812
13813     var call = function(){
13814         var now = new Date().getTime();
13815         if(now - t >= d){
13816             clearInterval(id);
13817             id = null;
13818             fn.apply(scope, args || []);
13819         }
13820     };
13821     /**
13822      * Cancels any pending timeout and queues a new one
13823      * @param {Number} delay The milliseconds to delay
13824      * @param {Function} newFn (optional) Overrides function passed to constructor
13825      * @param {Object} newScope (optional) Overrides scope passed to constructor
13826      * @param {Array} newArgs (optional) Overrides args passed to constructor
13827      */
13828     this.delay = function(delay, newFn, newScope, newArgs){
13829         if(id && delay != d){
13830             this.cancel();
13831         }
13832         d = delay;
13833         t = new Date().getTime();
13834         fn = newFn || fn;
13835         scope = newScope || scope;
13836         args = newArgs || args;
13837         if(!id){
13838             id = setInterval(call, d);
13839         }
13840     };
13841
13842     /**
13843      * Cancel the last queued timeout
13844      */
13845     this.cancel = function(){
13846         if(id){
13847             clearInterval(id);
13848             id = null;
13849         }
13850     };
13851 };/*
13852  * Based on:
13853  * Ext JS Library 1.1.1
13854  * Copyright(c) 2006-2007, Ext JS, LLC.
13855  *
13856  * Originally Released Under LGPL - original licence link has changed is not relivant.
13857  *
13858  * Fork - LGPL
13859  * <script type="text/javascript">
13860  */
13861 /**
13862  * @class Roo.util.TaskRunner
13863  * Manage background tasks - not sure why this is better that setInterval?
13864  * @static
13865  *
13866  */
13867  
13868 Roo.util.TaskRunner = function(interval){
13869     interval = interval || 10;
13870     var tasks = [], removeQueue = [];
13871     var id = 0;
13872     var running = false;
13873
13874     var stopThread = function(){
13875         running = false;
13876         clearInterval(id);
13877         id = 0;
13878     };
13879
13880     var startThread = function(){
13881         if(!running){
13882             running = true;
13883             id = setInterval(runTasks, interval);
13884         }
13885     };
13886
13887     var removeTask = function(task){
13888         removeQueue.push(task);
13889         if(task.onStop){
13890             task.onStop();
13891         }
13892     };
13893
13894     var runTasks = function(){
13895         if(removeQueue.length > 0){
13896             for(var i = 0, len = removeQueue.length; i < len; i++){
13897                 tasks.remove(removeQueue[i]);
13898             }
13899             removeQueue = [];
13900             if(tasks.length < 1){
13901                 stopThread();
13902                 return;
13903             }
13904         }
13905         var now = new Date().getTime();
13906         for(var i = 0, len = tasks.length; i < len; ++i){
13907             var t = tasks[i];
13908             var itime = now - t.taskRunTime;
13909             if(t.interval <= itime){
13910                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13911                 t.taskRunTime = now;
13912                 if(rt === false || t.taskRunCount === t.repeat){
13913                     removeTask(t);
13914                     return;
13915                 }
13916             }
13917             if(t.duration && t.duration <= (now - t.taskStartTime)){
13918                 removeTask(t);
13919             }
13920         }
13921     };
13922
13923     /**
13924      * Queues a new task.
13925      * @param {Object} task
13926      *
13927      * Task property : interval = how frequent to run.
13928      * Task object should implement
13929      * function run()
13930      * Task object may implement
13931      * function onStop()
13932      */
13933     this.start = function(task){
13934         tasks.push(task);
13935         task.taskStartTime = new Date().getTime();
13936         task.taskRunTime = 0;
13937         task.taskRunCount = 0;
13938         startThread();
13939         return task;
13940     };
13941     /**
13942      * Stop  new task.
13943      * @param {Object} task
13944      */
13945     this.stop = function(task){
13946         removeTask(task);
13947         return task;
13948     };
13949     /**
13950      * Stop all Tasks
13951      */
13952     this.stopAll = function(){
13953         stopThread();
13954         for(var i = 0, len = tasks.length; i < len; i++){
13955             if(tasks[i].onStop){
13956                 tasks[i].onStop();
13957             }
13958         }
13959         tasks = [];
13960         removeQueue = [];
13961     };
13962 };
13963
13964 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13965  * Based on:
13966  * Ext JS Library 1.1.1
13967  * Copyright(c) 2006-2007, Ext JS, LLC.
13968  *
13969  * Originally Released Under LGPL - original licence link has changed is not relivant.
13970  *
13971  * Fork - LGPL
13972  * <script type="text/javascript">
13973  */
13974
13975  
13976 /**
13977  * @class Roo.util.MixedCollection
13978  * @extends Roo.util.Observable
13979  * A Collection class that maintains both numeric indexes and keys and exposes events.
13980  * @constructor
13981  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13982  * collection (defaults to false)
13983  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13984  * and return the key value for that item.  This is used when available to look up the key on items that
13985  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13986  * equivalent to providing an implementation for the {@link #getKey} method.
13987  */
13988 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13989     this.items = [];
13990     this.map = {};
13991     this.keys = [];
13992     this.length = 0;
13993     this.addEvents({
13994         /**
13995          * @event clear
13996          * Fires when the collection is cleared.
13997          */
13998         "clear" : true,
13999         /**
14000          * @event add
14001          * Fires when an item is added to the collection.
14002          * @param {Number} index The index at which the item was added.
14003          * @param {Object} o The item added.
14004          * @param {String} key The key associated with the added item.
14005          */
14006         "add" : true,
14007         /**
14008          * @event replace
14009          * Fires when an item is replaced in the collection.
14010          * @param {String} key he key associated with the new added.
14011          * @param {Object} old The item being replaced.
14012          * @param {Object} new The new item.
14013          */
14014         "replace" : true,
14015         /**
14016          * @event remove
14017          * Fires when an item is removed from the collection.
14018          * @param {Object} o The item being removed.
14019          * @param {String} key (optional) The key associated with the removed item.
14020          */
14021         "remove" : true,
14022         "sort" : true
14023     });
14024     this.allowFunctions = allowFunctions === true;
14025     if(keyFn){
14026         this.getKey = keyFn;
14027     }
14028     Roo.util.MixedCollection.superclass.constructor.call(this);
14029 };
14030
14031 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14032     allowFunctions : false,
14033     
14034 /**
14035  * Adds an item to the collection.
14036  * @param {String} key The key to associate with the item
14037  * @param {Object} o The item to add.
14038  * @return {Object} The item added.
14039  */
14040     add : function(key, o){
14041         if(arguments.length == 1){
14042             o = arguments[0];
14043             key = this.getKey(o);
14044         }
14045         if(typeof key == "undefined" || key === null){
14046             this.length++;
14047             this.items.push(o);
14048             this.keys.push(null);
14049         }else{
14050             var old = this.map[key];
14051             if(old){
14052                 return this.replace(key, o);
14053             }
14054             this.length++;
14055             this.items.push(o);
14056             this.map[key] = o;
14057             this.keys.push(key);
14058         }
14059         this.fireEvent("add", this.length-1, o, key);
14060         return o;
14061     },
14062        
14063 /**
14064   * MixedCollection has a generic way to fetch keys if you implement getKey.
14065 <pre><code>
14066 // normal way
14067 var mc = new Roo.util.MixedCollection();
14068 mc.add(someEl.dom.id, someEl);
14069 mc.add(otherEl.dom.id, otherEl);
14070 //and so on
14071
14072 // using getKey
14073 var mc = new Roo.util.MixedCollection();
14074 mc.getKey = function(el){
14075    return el.dom.id;
14076 };
14077 mc.add(someEl);
14078 mc.add(otherEl);
14079
14080 // or via the constructor
14081 var mc = new Roo.util.MixedCollection(false, function(el){
14082    return el.dom.id;
14083 });
14084 mc.add(someEl);
14085 mc.add(otherEl);
14086 </code></pre>
14087  * @param o {Object} The item for which to find the key.
14088  * @return {Object} The key for the passed item.
14089  */
14090     getKey : function(o){
14091          return o.id; 
14092     },
14093    
14094 /**
14095  * Replaces an item in the collection.
14096  * @param {String} key The key associated with the item to replace, or the item to replace.
14097  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14098  * @return {Object}  The new item.
14099  */
14100     replace : function(key, o){
14101         if(arguments.length == 1){
14102             o = arguments[0];
14103             key = this.getKey(o);
14104         }
14105         var old = this.item(key);
14106         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14107              return this.add(key, o);
14108         }
14109         var index = this.indexOfKey(key);
14110         this.items[index] = o;
14111         this.map[key] = o;
14112         this.fireEvent("replace", key, old, o);
14113         return o;
14114     },
14115    
14116 /**
14117  * Adds all elements of an Array or an Object to the collection.
14118  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14119  * an Array of values, each of which are added to the collection.
14120  */
14121     addAll : function(objs){
14122         if(arguments.length > 1 || objs instanceof Array){
14123             var args = arguments.length > 1 ? arguments : objs;
14124             for(var i = 0, len = args.length; i < len; i++){
14125                 this.add(args[i]);
14126             }
14127         }else{
14128             for(var key in objs){
14129                 if(this.allowFunctions || typeof objs[key] != "function"){
14130                     this.add(key, objs[key]);
14131                 }
14132             }
14133         }
14134     },
14135    
14136 /**
14137  * Executes the specified function once for every item in the collection, passing each
14138  * item as the first and only parameter. returning false from the function will stop the iteration.
14139  * @param {Function} fn The function to execute for each item.
14140  * @param {Object} scope (optional) The scope in which to execute the function.
14141  */
14142     each : function(fn, scope){
14143         var items = [].concat(this.items); // each safe for removal
14144         for(var i = 0, len = items.length; i < len; i++){
14145             if(fn.call(scope || items[i], items[i], i, len) === false){
14146                 break;
14147             }
14148         }
14149     },
14150    
14151 /**
14152  * Executes the specified function once for every key in the collection, passing each
14153  * key, and its associated item as the first two parameters.
14154  * @param {Function} fn The function to execute for each item.
14155  * @param {Object} scope (optional) The scope in which to execute the function.
14156  */
14157     eachKey : function(fn, scope){
14158         for(var i = 0, len = this.keys.length; i < len; i++){
14159             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14160         }
14161     },
14162    
14163 /**
14164  * Returns the first item in the collection which elicits a true return value from the
14165  * passed selection function.
14166  * @param {Function} fn The selection function to execute for each item.
14167  * @param {Object} scope (optional) The scope in which to execute the function.
14168  * @return {Object} The first item in the collection which returned true from the selection function.
14169  */
14170     find : function(fn, scope){
14171         for(var i = 0, len = this.items.length; i < len; i++){
14172             if(fn.call(scope || window, this.items[i], this.keys[i])){
14173                 return this.items[i];
14174             }
14175         }
14176         return null;
14177     },
14178    
14179 /**
14180  * Inserts an item at the specified index in the collection.
14181  * @param {Number} index The index to insert the item at.
14182  * @param {String} key The key to associate with the new item, or the item itself.
14183  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14184  * @return {Object} The item inserted.
14185  */
14186     insert : function(index, key, o){
14187         if(arguments.length == 2){
14188             o = arguments[1];
14189             key = this.getKey(o);
14190         }
14191         if(index >= this.length){
14192             return this.add(key, o);
14193         }
14194         this.length++;
14195         this.items.splice(index, 0, o);
14196         if(typeof key != "undefined" && key != null){
14197             this.map[key] = o;
14198         }
14199         this.keys.splice(index, 0, key);
14200         this.fireEvent("add", index, o, key);
14201         return o;
14202     },
14203    
14204 /**
14205  * Removed an item from the collection.
14206  * @param {Object} o The item to remove.
14207  * @return {Object} The item removed.
14208  */
14209     remove : function(o){
14210         return this.removeAt(this.indexOf(o));
14211     },
14212    
14213 /**
14214  * Remove an item from a specified index in the collection.
14215  * @param {Number} index The index within the collection of the item to remove.
14216  */
14217     removeAt : function(index){
14218         if(index < this.length && index >= 0){
14219             this.length--;
14220             var o = this.items[index];
14221             this.items.splice(index, 1);
14222             var key = this.keys[index];
14223             if(typeof key != "undefined"){
14224                 delete this.map[key];
14225             }
14226             this.keys.splice(index, 1);
14227             this.fireEvent("remove", o, key);
14228         }
14229     },
14230    
14231 /**
14232  * Removed an item associated with the passed key fom the collection.
14233  * @param {String} key The key of the item to remove.
14234  */
14235     removeKey : function(key){
14236         return this.removeAt(this.indexOfKey(key));
14237     },
14238    
14239 /**
14240  * Returns the number of items in the collection.
14241  * @return {Number} the number of items in the collection.
14242  */
14243     getCount : function(){
14244         return this.length; 
14245     },
14246    
14247 /**
14248  * Returns index within the collection of the passed Object.
14249  * @param {Object} o The item to find the index of.
14250  * @return {Number} index of the item.
14251  */
14252     indexOf : function(o){
14253         if(!this.items.indexOf){
14254             for(var i = 0, len = this.items.length; i < len; i++){
14255                 if(this.items[i] == o) {
14256                     return i;
14257                 }
14258             }
14259             return -1;
14260         }else{
14261             return this.items.indexOf(o);
14262         }
14263     },
14264    
14265 /**
14266  * Returns index within the collection of the passed key.
14267  * @param {String} key The key to find the index of.
14268  * @return {Number} index of the key.
14269  */
14270     indexOfKey : function(key){
14271         if(!this.keys.indexOf){
14272             for(var i = 0, len = this.keys.length; i < len; i++){
14273                 if(this.keys[i] == key) {
14274                     return i;
14275                 }
14276             }
14277             return -1;
14278         }else{
14279             return this.keys.indexOf(key);
14280         }
14281     },
14282    
14283 /**
14284  * Returns the item associated with the passed key OR index. Key has priority over index.
14285  * @param {String/Number} key The key or index of the item.
14286  * @return {Object} The item associated with the passed key.
14287  */
14288     item : function(key){
14289         if (key === 'length') {
14290             return null;
14291         }
14292         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14293         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14294     },
14295     
14296 /**
14297  * Returns the item at the specified index.
14298  * @param {Number} index The index of the item.
14299  * @return {Object}
14300  */
14301     itemAt : function(index){
14302         return this.items[index];
14303     },
14304     
14305 /**
14306  * Returns the item associated with the passed key.
14307  * @param {String/Number} key The key of the item.
14308  * @return {Object} The item associated with the passed key.
14309  */
14310     key : function(key){
14311         return this.map[key];
14312     },
14313    
14314 /**
14315  * Returns true if the collection contains the passed Object as an item.
14316  * @param {Object} o  The Object to look for in the collection.
14317  * @return {Boolean} True if the collection contains the Object as an item.
14318  */
14319     contains : function(o){
14320         return this.indexOf(o) != -1;
14321     },
14322    
14323 /**
14324  * Returns true if the collection contains the passed Object as a key.
14325  * @param {String} key The key to look for in the collection.
14326  * @return {Boolean} True if the collection contains the Object as a key.
14327  */
14328     containsKey : function(key){
14329         return typeof this.map[key] != "undefined";
14330     },
14331    
14332 /**
14333  * Removes all items from the collection.
14334  */
14335     clear : function(){
14336         this.length = 0;
14337         this.items = [];
14338         this.keys = [];
14339         this.map = {};
14340         this.fireEvent("clear");
14341     },
14342    
14343 /**
14344  * Returns the first item in the collection.
14345  * @return {Object} the first item in the collection..
14346  */
14347     first : function(){
14348         return this.items[0]; 
14349     },
14350    
14351 /**
14352  * Returns the last item in the collection.
14353  * @return {Object} the last item in the collection..
14354  */
14355     last : function(){
14356         return this.items[this.length-1];   
14357     },
14358     
14359     _sort : function(property, dir, fn){
14360         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14361         fn = fn || function(a, b){
14362             return a-b;
14363         };
14364         var c = [], k = this.keys, items = this.items;
14365         for(var i = 0, len = items.length; i < len; i++){
14366             c[c.length] = {key: k[i], value: items[i], index: i};
14367         }
14368         c.sort(function(a, b){
14369             var v = fn(a[property], b[property]) * dsc;
14370             if(v == 0){
14371                 v = (a.index < b.index ? -1 : 1);
14372             }
14373             return v;
14374         });
14375         for(var i = 0, len = c.length; i < len; i++){
14376             items[i] = c[i].value;
14377             k[i] = c[i].key;
14378         }
14379         this.fireEvent("sort", this);
14380     },
14381     
14382     /**
14383      * Sorts this collection with the passed comparison function
14384      * @param {String} direction (optional) "ASC" or "DESC"
14385      * @param {Function} fn (optional) comparison function
14386      */
14387     sort : function(dir, fn){
14388         this._sort("value", dir, fn);
14389     },
14390     
14391     /**
14392      * Sorts this collection by keys
14393      * @param {String} direction (optional) "ASC" or "DESC"
14394      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14395      */
14396     keySort : function(dir, fn){
14397         this._sort("key", dir, fn || function(a, b){
14398             return String(a).toUpperCase()-String(b).toUpperCase();
14399         });
14400     },
14401     
14402     /**
14403      * Returns a range of items in this collection
14404      * @param {Number} startIndex (optional) defaults to 0
14405      * @param {Number} endIndex (optional) default to the last item
14406      * @return {Array} An array of items
14407      */
14408     getRange : function(start, end){
14409         var items = this.items;
14410         if(items.length < 1){
14411             return [];
14412         }
14413         start = start || 0;
14414         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14415         var r = [];
14416         if(start <= end){
14417             for(var i = start; i <= end; i++) {
14418                     r[r.length] = items[i];
14419             }
14420         }else{
14421             for(var i = start; i >= end; i--) {
14422                     r[r.length] = items[i];
14423             }
14424         }
14425         return r;
14426     },
14427         
14428     /**
14429      * Filter the <i>objects</i> in this collection by a specific property. 
14430      * Returns a new collection that has been filtered.
14431      * @param {String} property A property on your objects
14432      * @param {String/RegExp} value Either string that the property values 
14433      * should start with or a RegExp to test against the property
14434      * @return {MixedCollection} The new filtered collection
14435      */
14436     filter : function(property, value){
14437         if(!value.exec){ // not a regex
14438             value = String(value);
14439             if(value.length == 0){
14440                 return this.clone();
14441             }
14442             value = new RegExp("^" + Roo.escapeRe(value), "i");
14443         }
14444         return this.filterBy(function(o){
14445             return o && value.test(o[property]);
14446         });
14447         },
14448     
14449     /**
14450      * Filter by a function. * Returns a new collection that has been filtered.
14451      * The passed function will be called with each 
14452      * object in the collection. If the function returns true, the value is included 
14453      * otherwise it is filtered.
14454      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14455      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14456      * @return {MixedCollection} The new filtered collection
14457      */
14458     filterBy : function(fn, scope){
14459         var r = new Roo.util.MixedCollection();
14460         r.getKey = this.getKey;
14461         var k = this.keys, it = this.items;
14462         for(var i = 0, len = it.length; i < len; i++){
14463             if(fn.call(scope||this, it[i], k[i])){
14464                                 r.add(k[i], it[i]);
14465                         }
14466         }
14467         return r;
14468     },
14469     
14470     /**
14471      * Creates a duplicate of this collection
14472      * @return {MixedCollection}
14473      */
14474     clone : function(){
14475         var r = new Roo.util.MixedCollection();
14476         var k = this.keys, it = this.items;
14477         for(var i = 0, len = it.length; i < len; i++){
14478             r.add(k[i], it[i]);
14479         }
14480         r.getKey = this.getKey;
14481         return r;
14482     }
14483 });
14484 /**
14485  * Returns the item associated with the passed key or index.
14486  * @method
14487  * @param {String/Number} key The key or index of the item.
14488  * @return {Object} The item associated with the passed key.
14489  */
14490 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14491  * Based on:
14492  * Ext JS Library 1.1.1
14493  * Copyright(c) 2006-2007, Ext JS, LLC.
14494  *
14495  * Originally Released Under LGPL - original licence link has changed is not relivant.
14496  *
14497  * Fork - LGPL
14498  * <script type="text/javascript">
14499  */
14500 /**
14501  * @class Roo.util.JSON
14502  * Modified version of Douglas Crockford"s json.js that doesn"t
14503  * mess with the Object prototype 
14504  * http://www.json.org/js.html
14505  * @static
14506  */
14507 Roo.util.JSON = new (function(){
14508     var useHasOwn = {}.hasOwnProperty ? true : false;
14509     
14510     // crashes Safari in some instances
14511     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14512     
14513     var pad = function(n) {
14514         return n < 10 ? "0" + n : n;
14515     };
14516     
14517     var m = {
14518         "\b": '\\b',
14519         "\t": '\\t',
14520         "\n": '\\n',
14521         "\f": '\\f',
14522         "\r": '\\r',
14523         '"' : '\\"',
14524         "\\": '\\\\'
14525     };
14526
14527     var encodeString = function(s){
14528         if (/["\\\x00-\x1f]/.test(s)) {
14529             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14530                 var c = m[b];
14531                 if(c){
14532                     return c;
14533                 }
14534                 c = b.charCodeAt();
14535                 return "\\u00" +
14536                     Math.floor(c / 16).toString(16) +
14537                     (c % 16).toString(16);
14538             }) + '"';
14539         }
14540         return '"' + s + '"';
14541     };
14542     
14543     var encodeArray = function(o){
14544         var a = ["["], b, i, l = o.length, v;
14545             for (i = 0; i < l; i += 1) {
14546                 v = o[i];
14547                 switch (typeof v) {
14548                     case "undefined":
14549                     case "function":
14550                     case "unknown":
14551                         break;
14552                     default:
14553                         if (b) {
14554                             a.push(',');
14555                         }
14556                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14557                         b = true;
14558                 }
14559             }
14560             a.push("]");
14561             return a.join("");
14562     };
14563     
14564     var encodeDate = function(o){
14565         return '"' + o.getFullYear() + "-" +
14566                 pad(o.getMonth() + 1) + "-" +
14567                 pad(o.getDate()) + "T" +
14568                 pad(o.getHours()) + ":" +
14569                 pad(o.getMinutes()) + ":" +
14570                 pad(o.getSeconds()) + '"';
14571     };
14572     
14573     /**
14574      * Encodes an Object, Array or other value
14575      * @param {Mixed} o The variable to encode
14576      * @return {String} The JSON string
14577      */
14578     this.encode = function(o)
14579     {
14580         // should this be extended to fully wrap stringify..
14581         
14582         if(typeof o == "undefined" || o === null){
14583             return "null";
14584         }else if(o instanceof Array){
14585             return encodeArray(o);
14586         }else if(o instanceof Date){
14587             return encodeDate(o);
14588         }else if(typeof o == "string"){
14589             return encodeString(o);
14590         }else if(typeof o == "number"){
14591             return isFinite(o) ? String(o) : "null";
14592         }else if(typeof o == "boolean"){
14593             return String(o);
14594         }else {
14595             var a = ["{"], b, i, v;
14596             for (i in o) {
14597                 if(!useHasOwn || o.hasOwnProperty(i)) {
14598                     v = o[i];
14599                     switch (typeof v) {
14600                     case "undefined":
14601                     case "function":
14602                     case "unknown":
14603                         break;
14604                     default:
14605                         if(b){
14606                             a.push(',');
14607                         }
14608                         a.push(this.encode(i), ":",
14609                                 v === null ? "null" : this.encode(v));
14610                         b = true;
14611                     }
14612                 }
14613             }
14614             a.push("}");
14615             return a.join("");
14616         }
14617     };
14618     
14619     /**
14620      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14621      * @param {String} json The JSON string
14622      * @return {Object} The resulting object
14623      */
14624     this.decode = function(json){
14625         
14626         return  /** eval:var:json */ eval("(" + json + ')');
14627     };
14628 })();
14629 /** 
14630  * Shorthand for {@link Roo.util.JSON#encode}
14631  * @member Roo encode 
14632  * @method */
14633 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14634 /** 
14635  * Shorthand for {@link Roo.util.JSON#decode}
14636  * @member Roo decode 
14637  * @method */
14638 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14639 /*
14640  * Based on:
14641  * Ext JS Library 1.1.1
14642  * Copyright(c) 2006-2007, Ext JS, LLC.
14643  *
14644  * Originally Released Under LGPL - original licence link has changed is not relivant.
14645  *
14646  * Fork - LGPL
14647  * <script type="text/javascript">
14648  */
14649  
14650 /**
14651  * @class Roo.util.Format
14652  * Reusable data formatting functions
14653  * @static
14654  */
14655 Roo.util.Format = function(){
14656     var trimRe = /^\s+|\s+$/g;
14657     return {
14658         /**
14659          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14660          * @param {String} value The string to truncate
14661          * @param {Number} length The maximum length to allow before truncating
14662          * @return {String} The converted text
14663          */
14664         ellipsis : function(value, len){
14665             if(value && value.length > len){
14666                 return value.substr(0, len-3)+"...";
14667             }
14668             return value;
14669         },
14670
14671         /**
14672          * Checks a reference and converts it to empty string if it is undefined
14673          * @param {Mixed} value Reference to check
14674          * @return {Mixed} Empty string if converted, otherwise the original value
14675          */
14676         undef : function(value){
14677             return typeof value != "undefined" ? value : "";
14678         },
14679
14680         /**
14681          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14682          * @param {String} value The string to encode
14683          * @return {String} The encoded text
14684          */
14685         htmlEncode : function(value){
14686             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
14687         },
14688
14689         /**
14690          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14691          * @param {String} value The string to decode
14692          * @return {String} The decoded text
14693          */
14694         htmlDecode : function(value){
14695             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
14696         },
14697
14698         /**
14699          * Trims any whitespace from either side of a string
14700          * @param {String} value The text to trim
14701          * @return {String} The trimmed text
14702          */
14703         trim : function(value){
14704             return String(value).replace(trimRe, "");
14705         },
14706
14707         /**
14708          * Returns a substring from within an original string
14709          * @param {String} value The original text
14710          * @param {Number} start The start index of the substring
14711          * @param {Number} length The length of the substring
14712          * @return {String} The substring
14713          */
14714         substr : function(value, start, length){
14715             return String(value).substr(start, length);
14716         },
14717
14718         /**
14719          * Converts a string to all lower case letters
14720          * @param {String} value The text to convert
14721          * @return {String} The converted text
14722          */
14723         lowercase : function(value){
14724             return String(value).toLowerCase();
14725         },
14726
14727         /**
14728          * Converts a string to all upper case letters
14729          * @param {String} value The text to convert
14730          * @return {String} The converted text
14731          */
14732         uppercase : function(value){
14733             return String(value).toUpperCase();
14734         },
14735
14736         /**
14737          * Converts the first character only of a string to upper case
14738          * @param {String} value The text to convert
14739          * @return {String} The converted text
14740          */
14741         capitalize : function(value){
14742             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
14743         },
14744
14745         // private
14746         call : function(value, fn){
14747             if(arguments.length > 2){
14748                 var args = Array.prototype.slice.call(arguments, 2);
14749                 args.unshift(value);
14750                  
14751                 return /** eval:var:value */  eval(fn).apply(window, args);
14752             }else{
14753                 /** eval:var:value */
14754                 return /** eval:var:value */ eval(fn).call(window, value);
14755             }
14756         },
14757
14758        
14759         /**
14760          * safer version of Math.toFixed..??/
14761          * @param {Number/String} value The numeric value to format
14762          * @param {Number/String} value Decimal places 
14763          * @return {String} The formatted currency string
14764          */
14765         toFixed : function(v, n)
14766         {
14767             // why not use to fixed - precision is buggered???
14768             if (!n) {
14769                 return Math.round(v-0);
14770             }
14771             var fact = Math.pow(10,n+1);
14772             v = (Math.round((v-0)*fact))/fact;
14773             var z = (''+fact).substring(2);
14774             if (v == Math.floor(v)) {
14775                 return Math.floor(v) + '.' + z;
14776             }
14777             
14778             // now just padd decimals..
14779             var ps = String(v).split('.');
14780             var fd = (ps[1] + z);
14781             var r = fd.substring(0,n); 
14782             var rm = fd.substring(n); 
14783             if (rm < 5) {
14784                 return ps[0] + '.' + r;
14785             }
14786             r*=1; // turn it into a number;
14787             r++;
14788             if (String(r).length != n) {
14789                 ps[0]*=1;
14790                 ps[0]++;
14791                 r = String(r).substring(1); // chop the end off.
14792             }
14793             
14794             return ps[0] + '.' + r;
14795              
14796         },
14797         
14798         /**
14799          * Format a number as US currency
14800          * @param {Number/String} value The numeric value to format
14801          * @return {String} The formatted currency string
14802          */
14803         usMoney : function(v){
14804             return '$' + Roo.util.Format.number(v);
14805         },
14806         
14807         /**
14808          * Format a number
14809          * eventually this should probably emulate php's number_format
14810          * @param {Number/String} value The numeric value to format
14811          * @param {Number} decimals number of decimal places
14812          * @param {String} delimiter for thousands (default comma)
14813          * @return {String} The formatted currency string
14814          */
14815         number : function(v, decimals, thousandsDelimiter)
14816         {
14817             // multiply and round.
14818             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
14819             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
14820             
14821             var mul = Math.pow(10, decimals);
14822             var zero = String(mul).substring(1);
14823             v = (Math.round((v-0)*mul))/mul;
14824             
14825             // if it's '0' number.. then
14826             
14827             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
14828             v = String(v);
14829             var ps = v.split('.');
14830             var whole = ps[0];
14831             
14832             var r = /(\d+)(\d{3})/;
14833             // add comma's
14834             
14835             if(thousandsDelimiter.length != 0) {
14836                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
14837             } 
14838             
14839             var sub = ps[1] ?
14840                     // has decimals..
14841                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
14842                     // does not have decimals
14843                     (decimals ? ('.' + zero) : '');
14844             
14845             
14846             return whole + sub ;
14847         },
14848         
14849         /**
14850          * Parse a value into a formatted date using the specified format pattern.
14851          * @param {Mixed} value The value to format
14852          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14853          * @return {String} The formatted date string
14854          */
14855         date : function(v, format){
14856             if(!v){
14857                 return "";
14858             }
14859             if(!(v instanceof Date)){
14860                 v = new Date(Date.parse(v));
14861             }
14862             return v.dateFormat(format || Roo.util.Format.defaults.date);
14863         },
14864
14865         /**
14866          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14867          * @param {String} format Any valid date format string
14868          * @return {Function} The date formatting function
14869          */
14870         dateRenderer : function(format){
14871             return function(v){
14872                 return Roo.util.Format.date(v, format);  
14873             };
14874         },
14875
14876         // private
14877         stripTagsRE : /<\/?[^>]+>/gi,
14878         
14879         /**
14880          * Strips all HTML tags
14881          * @param {Mixed} value The text from which to strip tags
14882          * @return {String} The stripped text
14883          */
14884         stripTags : function(v){
14885             return !v ? v : String(v).replace(this.stripTagsRE, "");
14886         },
14887         
14888         /**
14889          * Size in Mb,Gb etc.
14890          * @param {Number} value The number to be formated
14891          * @param {number} decimals how many decimal places
14892          * @return {String} the formated string
14893          */
14894         size : function(value, decimals)
14895         {
14896             var sizes = ['b', 'k', 'M', 'G', 'T'];
14897             if (value == 0) {
14898                 return 0;
14899             }
14900             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14901             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14902         }
14903         
14904         
14905         
14906     };
14907 }();
14908 Roo.util.Format.defaults = {
14909     date : 'd/M/Y'
14910 };/*
14911  * Based on:
14912  * Ext JS Library 1.1.1
14913  * Copyright(c) 2006-2007, Ext JS, LLC.
14914  *
14915  * Originally Released Under LGPL - original licence link has changed is not relivant.
14916  *
14917  * Fork - LGPL
14918  * <script type="text/javascript">
14919  */
14920
14921
14922  
14923
14924 /**
14925  * @class Roo.MasterTemplate
14926  * @extends Roo.Template
14927  * Provides a template that can have child templates. The syntax is:
14928 <pre><code>
14929 var t = new Roo.MasterTemplate(
14930         '&lt;select name="{name}"&gt;',
14931                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14932         '&lt;/select&gt;'
14933 );
14934 t.add('options', {value: 'foo', text: 'bar'});
14935 // or you can add multiple child elements in one shot
14936 t.addAll('options', [
14937     {value: 'foo', text: 'bar'},
14938     {value: 'foo2', text: 'bar2'},
14939     {value: 'foo3', text: 'bar3'}
14940 ]);
14941 // then append, applying the master template values
14942 t.append('my-form', {name: 'my-select'});
14943 </code></pre>
14944 * A name attribute for the child template is not required if you have only one child
14945 * template or you want to refer to them by index.
14946  */
14947 Roo.MasterTemplate = function(){
14948     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14949     this.originalHtml = this.html;
14950     var st = {};
14951     var m, re = this.subTemplateRe;
14952     re.lastIndex = 0;
14953     var subIndex = 0;
14954     while(m = re.exec(this.html)){
14955         var name = m[1], content = m[2];
14956         st[subIndex] = {
14957             name: name,
14958             index: subIndex,
14959             buffer: [],
14960             tpl : new Roo.Template(content)
14961         };
14962         if(name){
14963             st[name] = st[subIndex];
14964         }
14965         st[subIndex].tpl.compile();
14966         st[subIndex].tpl.call = this.call.createDelegate(this);
14967         subIndex++;
14968     }
14969     this.subCount = subIndex;
14970     this.subs = st;
14971 };
14972 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14973     /**
14974     * The regular expression used to match sub templates
14975     * @type RegExp
14976     * @property
14977     */
14978     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14979
14980     /**
14981      * Applies the passed values to a child template.
14982      * @param {String/Number} name (optional) The name or index of the child template
14983      * @param {Array/Object} values The values to be applied to the template
14984      * @return {MasterTemplate} this
14985      */
14986      add : function(name, values){
14987         if(arguments.length == 1){
14988             values = arguments[0];
14989             name = 0;
14990         }
14991         var s = this.subs[name];
14992         s.buffer[s.buffer.length] = s.tpl.apply(values);
14993         return this;
14994     },
14995
14996     /**
14997      * Applies all the passed values to a child template.
14998      * @param {String/Number} name (optional) The name or index of the child template
14999      * @param {Array} values The values to be applied to the template, this should be an array of objects.
15000      * @param {Boolean} reset (optional) True to reset the template first
15001      * @return {MasterTemplate} this
15002      */
15003     fill : function(name, values, reset){
15004         var a = arguments;
15005         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15006             values = a[0];
15007             name = 0;
15008             reset = a[1];
15009         }
15010         if(reset){
15011             this.reset();
15012         }
15013         for(var i = 0, len = values.length; i < len; i++){
15014             this.add(name, values[i]);
15015         }
15016         return this;
15017     },
15018
15019     /**
15020      * Resets the template for reuse
15021      * @return {MasterTemplate} this
15022      */
15023      reset : function(){
15024         var s = this.subs;
15025         for(var i = 0; i < this.subCount; i++){
15026             s[i].buffer = [];
15027         }
15028         return this;
15029     },
15030
15031     applyTemplate : function(values){
15032         var s = this.subs;
15033         var replaceIndex = -1;
15034         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15035             return s[++replaceIndex].buffer.join("");
15036         });
15037         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15038     },
15039
15040     apply : function(){
15041         return this.applyTemplate.apply(this, arguments);
15042     },
15043
15044     compile : function(){return this;}
15045 });
15046
15047 /**
15048  * Alias for fill().
15049  * @method
15050  */
15051 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15052  /**
15053  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15054  * var tpl = Roo.MasterTemplate.from('element-id');
15055  * @param {String/HTMLElement} el
15056  * @param {Object} config
15057  * @static
15058  */
15059 Roo.MasterTemplate.from = function(el, config){
15060     el = Roo.getDom(el);
15061     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15062 };/*
15063  * Based on:
15064  * Ext JS Library 1.1.1
15065  * Copyright(c) 2006-2007, Ext JS, LLC.
15066  *
15067  * Originally Released Under LGPL - original licence link has changed is not relivant.
15068  *
15069  * Fork - LGPL
15070  * <script type="text/javascript">
15071  */
15072
15073  
15074 /**
15075  * @class Roo.util.CSS
15076  * Utility class for manipulating CSS rules
15077  * @static
15078
15079  */
15080 Roo.util.CSS = function(){
15081         var rules = null;
15082         var doc = document;
15083
15084     var camelRe = /(-[a-z])/gi;
15085     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15086
15087    return {
15088    /**
15089     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15090     * tag and appended to the HEAD of the document.
15091     * @param {String|Object} cssText The text containing the css rules
15092     * @param {String} id An id to add to the stylesheet for later removal
15093     * @return {StyleSheet}
15094     */
15095     createStyleSheet : function(cssText, id){
15096         var ss;
15097         var head = doc.getElementsByTagName("head")[0];
15098         var nrules = doc.createElement("style");
15099         nrules.setAttribute("type", "text/css");
15100         if(id){
15101             nrules.setAttribute("id", id);
15102         }
15103         if (typeof(cssText) != 'string') {
15104             // support object maps..
15105             // not sure if this a good idea.. 
15106             // perhaps it should be merged with the general css handling
15107             // and handle js style props.
15108             var cssTextNew = [];
15109             for(var n in cssText) {
15110                 var citems = [];
15111                 for(var k in cssText[n]) {
15112                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15113                 }
15114                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15115                 
15116             }
15117             cssText = cssTextNew.join("\n");
15118             
15119         }
15120        
15121        
15122        if(Roo.isIE){
15123            head.appendChild(nrules);
15124            ss = nrules.styleSheet;
15125            ss.cssText = cssText;
15126        }else{
15127            try{
15128                 nrules.appendChild(doc.createTextNode(cssText));
15129            }catch(e){
15130                nrules.cssText = cssText; 
15131            }
15132            head.appendChild(nrules);
15133            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15134        }
15135        this.cacheStyleSheet(ss);
15136        return ss;
15137    },
15138
15139    /**
15140     * Removes a style or link tag by id
15141     * @param {String} id The id of the tag
15142     */
15143    removeStyleSheet : function(id){
15144        var existing = doc.getElementById(id);
15145        if(existing){
15146            existing.parentNode.removeChild(existing);
15147        }
15148    },
15149
15150    /**
15151     * Dynamically swaps an existing stylesheet reference for a new one
15152     * @param {String} id The id of an existing link tag to remove
15153     * @param {String} url The href of the new stylesheet to include
15154     */
15155    swapStyleSheet : function(id, url){
15156        this.removeStyleSheet(id);
15157        var ss = doc.createElement("link");
15158        ss.setAttribute("rel", "stylesheet");
15159        ss.setAttribute("type", "text/css");
15160        ss.setAttribute("id", id);
15161        ss.setAttribute("href", url);
15162        doc.getElementsByTagName("head")[0].appendChild(ss);
15163    },
15164    
15165    /**
15166     * Refresh the rule cache if you have dynamically added stylesheets
15167     * @return {Object} An object (hash) of rules indexed by selector
15168     */
15169    refreshCache : function(){
15170        return this.getRules(true);
15171    },
15172
15173    // private
15174    cacheStyleSheet : function(stylesheet){
15175        if(!rules){
15176            rules = {};
15177        }
15178        try{// try catch for cross domain access issue
15179            var ssRules = stylesheet.cssRules || stylesheet.rules;
15180            for(var j = ssRules.length-1; j >= 0; --j){
15181                rules[ssRules[j].selectorText] = ssRules[j];
15182            }
15183        }catch(e){}
15184    },
15185    
15186    /**
15187     * Gets all css rules for the document
15188     * @param {Boolean} refreshCache true to refresh the internal cache
15189     * @return {Object} An object (hash) of rules indexed by selector
15190     */
15191    getRules : function(refreshCache){
15192                 if(rules == null || refreshCache){
15193                         rules = {};
15194                         var ds = doc.styleSheets;
15195                         for(var i =0, len = ds.length; i < len; i++){
15196                             try{
15197                         this.cacheStyleSheet(ds[i]);
15198                     }catch(e){} 
15199                 }
15200                 }
15201                 return rules;
15202         },
15203         
15204         /**
15205     * Gets an an individual CSS rule by selector(s)
15206     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15207     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15208     * @return {CSSRule} The CSS rule or null if one is not found
15209     */
15210    getRule : function(selector, refreshCache){
15211                 var rs = this.getRules(refreshCache);
15212                 if(!(selector instanceof Array)){
15213                     return rs[selector];
15214                 }
15215                 for(var i = 0; i < selector.length; i++){
15216                         if(rs[selector[i]]){
15217                                 return rs[selector[i]];
15218                         }
15219                 }
15220                 return null;
15221         },
15222         
15223         
15224         /**
15225     * Updates a rule property
15226     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15227     * @param {String} property The css property
15228     * @param {String} value The new value for the property
15229     * @return {Boolean} true If a rule was found and updated
15230     */
15231    updateRule : function(selector, property, value){
15232                 if(!(selector instanceof Array)){
15233                         var rule = this.getRule(selector);
15234                         if(rule){
15235                                 rule.style[property.replace(camelRe, camelFn)] = value;
15236                                 return true;
15237                         }
15238                 }else{
15239                         for(var i = 0; i < selector.length; i++){
15240                                 if(this.updateRule(selector[i], property, value)){
15241                                         return true;
15242                                 }
15243                         }
15244                 }
15245                 return false;
15246         }
15247    };   
15248 }();/*
15249  * Based on:
15250  * Ext JS Library 1.1.1
15251  * Copyright(c) 2006-2007, Ext JS, LLC.
15252  *
15253  * Originally Released Under LGPL - original licence link has changed is not relivant.
15254  *
15255  * Fork - LGPL
15256  * <script type="text/javascript">
15257  */
15258
15259  
15260
15261 /**
15262  * @class Roo.util.ClickRepeater
15263  * @extends Roo.util.Observable
15264  * 
15265  * A wrapper class which can be applied to any element. Fires a "click" event while the
15266  * mouse is pressed. The interval between firings may be specified in the config but
15267  * defaults to 10 milliseconds.
15268  * 
15269  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15270  * 
15271  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15272  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15273  * Similar to an autorepeat key delay.
15274  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15275  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15276  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15277  *           "interval" and "delay" are ignored. "immediate" is honored.
15278  * @cfg {Boolean} preventDefault True to prevent the default click event
15279  * @cfg {Boolean} stopDefault True to stop the default click event
15280  * 
15281  * @history
15282  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15283  *     2007-02-02 jvs Renamed to ClickRepeater
15284  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15285  *
15286  *  @constructor
15287  * @param {String/HTMLElement/Element} el The element to listen on
15288  * @param {Object} config
15289  **/
15290 Roo.util.ClickRepeater = function(el, config)
15291 {
15292     this.el = Roo.get(el);
15293     this.el.unselectable();
15294
15295     Roo.apply(this, config);
15296
15297     this.addEvents({
15298     /**
15299      * @event mousedown
15300      * Fires when the mouse button is depressed.
15301      * @param {Roo.util.ClickRepeater} this
15302      */
15303         "mousedown" : true,
15304     /**
15305      * @event click
15306      * Fires on a specified interval during the time the element is pressed.
15307      * @param {Roo.util.ClickRepeater} this
15308      */
15309         "click" : true,
15310     /**
15311      * @event mouseup
15312      * Fires when the mouse key is released.
15313      * @param {Roo.util.ClickRepeater} this
15314      */
15315         "mouseup" : true
15316     });
15317
15318     this.el.on("mousedown", this.handleMouseDown, this);
15319     if(this.preventDefault || this.stopDefault){
15320         this.el.on("click", function(e){
15321             if(this.preventDefault){
15322                 e.preventDefault();
15323             }
15324             if(this.stopDefault){
15325                 e.stopEvent();
15326             }
15327         }, this);
15328     }
15329
15330     // allow inline handler
15331     if(this.handler){
15332         this.on("click", this.handler,  this.scope || this);
15333     }
15334
15335     Roo.util.ClickRepeater.superclass.constructor.call(this);
15336 };
15337
15338 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15339     interval : 20,
15340     delay: 250,
15341     preventDefault : true,
15342     stopDefault : false,
15343     timer : 0,
15344
15345     // private
15346     handleMouseDown : function(){
15347         clearTimeout(this.timer);
15348         this.el.blur();
15349         if(this.pressClass){
15350             this.el.addClass(this.pressClass);
15351         }
15352         this.mousedownTime = new Date();
15353
15354         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15355         this.el.on("mouseout", this.handleMouseOut, this);
15356
15357         this.fireEvent("mousedown", this);
15358         this.fireEvent("click", this);
15359         
15360         this.timer = this.click.defer(this.delay || this.interval, this);
15361     },
15362
15363     // private
15364     click : function(){
15365         this.fireEvent("click", this);
15366         this.timer = this.click.defer(this.getInterval(), this);
15367     },
15368
15369     // private
15370     getInterval: function(){
15371         if(!this.accelerate){
15372             return this.interval;
15373         }
15374         var pressTime = this.mousedownTime.getElapsed();
15375         if(pressTime < 500){
15376             return 400;
15377         }else if(pressTime < 1700){
15378             return 320;
15379         }else if(pressTime < 2600){
15380             return 250;
15381         }else if(pressTime < 3500){
15382             return 180;
15383         }else if(pressTime < 4400){
15384             return 140;
15385         }else if(pressTime < 5300){
15386             return 80;
15387         }else if(pressTime < 6200){
15388             return 50;
15389         }else{
15390             return 10;
15391         }
15392     },
15393
15394     // private
15395     handleMouseOut : function(){
15396         clearTimeout(this.timer);
15397         if(this.pressClass){
15398             this.el.removeClass(this.pressClass);
15399         }
15400         this.el.on("mouseover", this.handleMouseReturn, this);
15401     },
15402
15403     // private
15404     handleMouseReturn : function(){
15405         this.el.un("mouseover", this.handleMouseReturn);
15406         if(this.pressClass){
15407             this.el.addClass(this.pressClass);
15408         }
15409         this.click();
15410     },
15411
15412     // private
15413     handleMouseUp : function(){
15414         clearTimeout(this.timer);
15415         this.el.un("mouseover", this.handleMouseReturn);
15416         this.el.un("mouseout", this.handleMouseOut);
15417         Roo.get(document).un("mouseup", this.handleMouseUp);
15418         this.el.removeClass(this.pressClass);
15419         this.fireEvent("mouseup", this);
15420     }
15421 });/**
15422  * @class Roo.util.Clipboard
15423  * @static
15424  * 
15425  * Clipboard UTILS
15426  * 
15427  **/
15428 Roo.util.Clipboard = {
15429     /**
15430      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15431      * @param {String} text to copy to clipboard
15432      */
15433     write : function(text) {
15434         // navigator clipboard api needs a secure context (https)
15435         if (navigator.clipboard && window.isSecureContext) {
15436             // navigator clipboard api method'
15437             navigator.clipboard.writeText(text);
15438             return ;
15439         } 
15440         // text area method
15441         var ta = document.createElement("textarea");
15442         ta.value = text;
15443         // make the textarea out of viewport
15444         ta.style.position = "fixed";
15445         ta.style.left = "-999999px";
15446         ta.style.top = "-999999px";
15447         document.body.appendChild(ta);
15448         ta.focus();
15449         ta.select();
15450         document.execCommand('copy');
15451         (function() {
15452             ta.remove();
15453         }).defer(100);
15454         
15455     }
15456         
15457 }
15458     /*
15459  * Based on:
15460  * Ext JS Library 1.1.1
15461  * Copyright(c) 2006-2007, Ext JS, LLC.
15462  *
15463  * Originally Released Under LGPL - original licence link has changed is not relivant.
15464  *
15465  * Fork - LGPL
15466  * <script type="text/javascript">
15467  */
15468
15469  
15470 /**
15471  * @class Roo.KeyNav
15472  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15473  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15474  * way to implement custom navigation schemes for any UI component.</p>
15475  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15476  * pageUp, pageDown, del, home, end.  Usage:</p>
15477  <pre><code>
15478 var nav = new Roo.KeyNav("my-element", {
15479     "left" : function(e){
15480         this.moveLeft(e.ctrlKey);
15481     },
15482     "right" : function(e){
15483         this.moveRight(e.ctrlKey);
15484     },
15485     "enter" : function(e){
15486         this.save();
15487     },
15488     scope : this
15489 });
15490 </code></pre>
15491  * @constructor
15492  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15493  * @param {Object} config The config
15494  */
15495 Roo.KeyNav = function(el, config){
15496     this.el = Roo.get(el);
15497     Roo.apply(this, config);
15498     if(!this.disabled){
15499         this.disabled = true;
15500         this.enable();
15501     }
15502 };
15503
15504 Roo.KeyNav.prototype = {
15505     /**
15506      * @cfg {Boolean} disabled
15507      * True to disable this KeyNav instance (defaults to false)
15508      */
15509     disabled : false,
15510     /**
15511      * @cfg {String} defaultEventAction
15512      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15513      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15514      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15515      */
15516     defaultEventAction: "stopEvent",
15517     /**
15518      * @cfg {Boolean} forceKeyDown
15519      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15520      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15521      * handle keydown instead of keypress.
15522      */
15523     forceKeyDown : false,
15524
15525     // private
15526     prepareEvent : function(e){
15527         var k = e.getKey();
15528         var h = this.keyToHandler[k];
15529         //if(h && this[h]){
15530         //    e.stopPropagation();
15531         //}
15532         if(Roo.isSafari && h && k >= 37 && k <= 40){
15533             e.stopEvent();
15534         }
15535     },
15536
15537     // private
15538     relay : function(e){
15539         var k = e.getKey();
15540         var h = this.keyToHandler[k];
15541         if(h && this[h]){
15542             if(this.doRelay(e, this[h], h) !== true){
15543                 e[this.defaultEventAction]();
15544             }
15545         }
15546     },
15547
15548     // private
15549     doRelay : function(e, h, hname){
15550         return h.call(this.scope || this, e);
15551     },
15552
15553     // possible handlers
15554     enter : false,
15555     left : false,
15556     right : false,
15557     up : false,
15558     down : false,
15559     tab : false,
15560     esc : false,
15561     pageUp : false,
15562     pageDown : false,
15563     del : false,
15564     home : false,
15565     end : false,
15566
15567     // quick lookup hash
15568     keyToHandler : {
15569         37 : "left",
15570         39 : "right",
15571         38 : "up",
15572         40 : "down",
15573         33 : "pageUp",
15574         34 : "pageDown",
15575         46 : "del",
15576         36 : "home",
15577         35 : "end",
15578         13 : "enter",
15579         27 : "esc",
15580         9  : "tab"
15581     },
15582
15583         /**
15584          * Enable this KeyNav
15585          */
15586         enable: function(){
15587                 if(this.disabled){
15588             // ie won't do special keys on keypress, no one else will repeat keys with keydown
15589             // the EventObject will normalize Safari automatically
15590             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15591                 this.el.on("keydown", this.relay,  this);
15592             }else{
15593                 this.el.on("keydown", this.prepareEvent,  this);
15594                 this.el.on("keypress", this.relay,  this);
15595             }
15596                     this.disabled = false;
15597                 }
15598         },
15599
15600         /**
15601          * Disable this KeyNav
15602          */
15603         disable: function(){
15604                 if(!this.disabled){
15605                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15606                 this.el.un("keydown", this.relay);
15607             }else{
15608                 this.el.un("keydown", this.prepareEvent);
15609                 this.el.un("keypress", this.relay);
15610             }
15611                     this.disabled = true;
15612                 }
15613         }
15614 };/*
15615  * Based on:
15616  * Ext JS Library 1.1.1
15617  * Copyright(c) 2006-2007, Ext JS, LLC.
15618  *
15619  * Originally Released Under LGPL - original licence link has changed is not relivant.
15620  *
15621  * Fork - LGPL
15622  * <script type="text/javascript">
15623  */
15624
15625  
15626 /**
15627  * @class Roo.KeyMap
15628  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15629  * The constructor accepts the same config object as defined by {@link #addBinding}.
15630  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15631  * combination it will call the function with this signature (if the match is a multi-key
15632  * combination the callback will still be called only once): (String key, Roo.EventObject e)
15633  * A KeyMap can also handle a string representation of keys.<br />
15634  * Usage:
15635  <pre><code>
15636 // map one key by key code
15637 var map = new Roo.KeyMap("my-element", {
15638     key: 13, // or Roo.EventObject.ENTER
15639     fn: myHandler,
15640     scope: myObject
15641 });
15642
15643 // map multiple keys to one action by string
15644 var map = new Roo.KeyMap("my-element", {
15645     key: "a\r\n\t",
15646     fn: myHandler,
15647     scope: myObject
15648 });
15649
15650 // map multiple keys to multiple actions by strings and array of codes
15651 var map = new Roo.KeyMap("my-element", [
15652     {
15653         key: [10,13],
15654         fn: function(){ alert("Return was pressed"); }
15655     }, {
15656         key: "abc",
15657         fn: function(){ alert('a, b or c was pressed'); }
15658     }, {
15659         key: "\t",
15660         ctrl:true,
15661         shift:true,
15662         fn: function(){ alert('Control + shift + tab was pressed.'); }
15663     }
15664 ]);
15665 </code></pre>
15666  * <b>Note: A KeyMap starts enabled</b>
15667  * @constructor
15668  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15669  * @param {Object} config The config (see {@link #addBinding})
15670  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15671  */
15672 Roo.KeyMap = function(el, config, eventName){
15673     this.el  = Roo.get(el);
15674     this.eventName = eventName || "keydown";
15675     this.bindings = [];
15676     if(config){
15677         this.addBinding(config);
15678     }
15679     this.enable();
15680 };
15681
15682 Roo.KeyMap.prototype = {
15683     /**
15684      * True to stop the event from bubbling and prevent the default browser action if the
15685      * key was handled by the KeyMap (defaults to false)
15686      * @type Boolean
15687      */
15688     stopEvent : false,
15689
15690     /**
15691      * Add a new binding to this KeyMap. The following config object properties are supported:
15692      * <pre>
15693 Property    Type             Description
15694 ----------  ---------------  ----------------------------------------------------------------------
15695 key         String/Array     A single keycode or an array of keycodes to handle
15696 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
15697 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
15698 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
15699 fn          Function         The function to call when KeyMap finds the expected key combination
15700 scope       Object           The scope of the callback function
15701 </pre>
15702      *
15703      * Usage:
15704      * <pre><code>
15705 // Create a KeyMap
15706 var map = new Roo.KeyMap(document, {
15707     key: Roo.EventObject.ENTER,
15708     fn: handleKey,
15709     scope: this
15710 });
15711
15712 //Add a new binding to the existing KeyMap later
15713 map.addBinding({
15714     key: 'abc',
15715     shift: true,
15716     fn: handleKey,
15717     scope: this
15718 });
15719 </code></pre>
15720      * @param {Object/Array} config A single KeyMap config or an array of configs
15721      */
15722         addBinding : function(config){
15723         if(config instanceof Array){
15724             for(var i = 0, len = config.length; i < len; i++){
15725                 this.addBinding(config[i]);
15726             }
15727             return;
15728         }
15729         var keyCode = config.key,
15730             shift = config.shift, 
15731             ctrl = config.ctrl, 
15732             alt = config.alt,
15733             fn = config.fn,
15734             scope = config.scope;
15735         if(typeof keyCode == "string"){
15736             var ks = [];
15737             var keyString = keyCode.toUpperCase();
15738             for(var j = 0, len = keyString.length; j < len; j++){
15739                 ks.push(keyString.charCodeAt(j));
15740             }
15741             keyCode = ks;
15742         }
15743         var keyArray = keyCode instanceof Array;
15744         var handler = function(e){
15745             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
15746                 var k = e.getKey();
15747                 if(keyArray){
15748                     for(var i = 0, len = keyCode.length; i < len; i++){
15749                         if(keyCode[i] == k){
15750                           if(this.stopEvent){
15751                               e.stopEvent();
15752                           }
15753                           fn.call(scope || window, k, e);
15754                           return;
15755                         }
15756                     }
15757                 }else{
15758                     if(k == keyCode){
15759                         if(this.stopEvent){
15760                            e.stopEvent();
15761                         }
15762                         fn.call(scope || window, k, e);
15763                     }
15764                 }
15765             }
15766         };
15767         this.bindings.push(handler);  
15768         },
15769
15770     /**
15771      * Shorthand for adding a single key listener
15772      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
15773      * following options:
15774      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
15775      * @param {Function} fn The function to call
15776      * @param {Object} scope (optional) The scope of the function
15777      */
15778     on : function(key, fn, scope){
15779         var keyCode, shift, ctrl, alt;
15780         if(typeof key == "object" && !(key instanceof Array)){
15781             keyCode = key.key;
15782             shift = key.shift;
15783             ctrl = key.ctrl;
15784             alt = key.alt;
15785         }else{
15786             keyCode = key;
15787         }
15788         this.addBinding({
15789             key: keyCode,
15790             shift: shift,
15791             ctrl: ctrl,
15792             alt: alt,
15793             fn: fn,
15794             scope: scope
15795         })
15796     },
15797
15798     // private
15799     handleKeyDown : function(e){
15800             if(this.enabled){ //just in case
15801             var b = this.bindings;
15802             for(var i = 0, len = b.length; i < len; i++){
15803                 b[i].call(this, e);
15804             }
15805             }
15806         },
15807         
15808         /**
15809          * Returns true if this KeyMap is enabled
15810          * @return {Boolean} 
15811          */
15812         isEnabled : function(){
15813             return this.enabled;  
15814         },
15815         
15816         /**
15817          * Enables this KeyMap
15818          */
15819         enable: function(){
15820                 if(!this.enabled){
15821                     this.el.on(this.eventName, this.handleKeyDown, this);
15822                     this.enabled = true;
15823                 }
15824         },
15825
15826         /**
15827          * Disable this KeyMap
15828          */
15829         disable: function(){
15830                 if(this.enabled){
15831                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
15832                     this.enabled = false;
15833                 }
15834         }
15835 };/*
15836  * Based on:
15837  * Ext JS Library 1.1.1
15838  * Copyright(c) 2006-2007, Ext JS, LLC.
15839  *
15840  * Originally Released Under LGPL - original licence link has changed is not relivant.
15841  *
15842  * Fork - LGPL
15843  * <script type="text/javascript">
15844  */
15845
15846  
15847 /**
15848  * @class Roo.util.TextMetrics
15849  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
15850  * wide, in pixels, a given block of text will be.
15851  * @static
15852  */
15853 Roo.util.TextMetrics = function(){
15854     var shared;
15855     return {
15856         /**
15857          * Measures the size of the specified text
15858          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15859          * that can affect the size of the rendered text
15860          * @param {String} text The text to measure
15861          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15862          * in order to accurately measure the text height
15863          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15864          */
15865         measure : function(el, text, fixedWidth){
15866             if(!shared){
15867                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
15868             }
15869             shared.bind(el);
15870             shared.setFixedWidth(fixedWidth || 'auto');
15871             return shared.getSize(text);
15872         },
15873
15874         /**
15875          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15876          * the overhead of multiple calls to initialize the style properties on each measurement.
15877          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15878          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15879          * in order to accurately measure the text height
15880          * @return {Roo.util.TextMetrics.Instance} instance The new instance
15881          */
15882         createInstance : function(el, fixedWidth){
15883             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15884         }
15885     };
15886 }();
15887
15888 /**
15889  * @class Roo.util.TextMetrics.Instance
15890  * Instance of  TextMetrics Calcuation
15891  * @constructor
15892  * Create a new TextMetrics Instance
15893  * @param {Object} bindto
15894  * @param {Boolean} fixedWidth
15895  */
15896
15897 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
15898 {
15899     var ml = new Roo.Element(document.createElement('div'));
15900     document.body.appendChild(ml.dom);
15901     ml.position('absolute');
15902     ml.setLeftTop(-1000, -1000);
15903     ml.hide();
15904
15905     if(fixedWidth){
15906         ml.setWidth(fixedWidth);
15907     }
15908      
15909     var instance = {
15910         /**
15911          * Returns the size of the specified text based on the internal element's style and width properties
15912          * @param {String} text The text to measure
15913          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15914          */
15915         getSize : function(text){
15916             ml.update(text);
15917             var s = ml.getSize();
15918             ml.update('');
15919             return s;
15920         },
15921
15922         /**
15923          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15924          * that can affect the size of the rendered text
15925          * @param {String/HTMLElement} el The element, dom node or id
15926          */
15927         bind : function(el){
15928             ml.setStyle(
15929                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15930             );
15931         },
15932
15933         /**
15934          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15935          * to set a fixed width in order to accurately measure the text height.
15936          * @param {Number} width The width to set on the element
15937          */
15938         setFixedWidth : function(width){
15939             ml.setWidth(width);
15940         },
15941
15942         /**
15943          * Returns the measured width of the specified text
15944          * @param {String} text The text to measure
15945          * @return {Number} width The width in pixels
15946          */
15947         getWidth : function(text){
15948             ml.dom.style.width = 'auto';
15949             return this.getSize(text).width;
15950         },
15951
15952         /**
15953          * Returns the measured height of the specified text.  For multiline text, be sure to call
15954          * {@link #setFixedWidth} if necessary.
15955          * @param {String} text The text to measure
15956          * @return {Number} height The height in pixels
15957          */
15958         getHeight : function(text){
15959             return this.getSize(text).height;
15960         }
15961     };
15962
15963     instance.bind(bindTo);
15964
15965     return instance;
15966 };
15967
15968 // backwards compat
15969 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15970  * Based on:
15971  * Ext JS Library 1.1.1
15972  * Copyright(c) 2006-2007, Ext JS, LLC.
15973  *
15974  * Originally Released Under LGPL - original licence link has changed is not relivant.
15975  *
15976  * Fork - LGPL
15977  * <script type="text/javascript">
15978  */
15979
15980 /**
15981  * @class Roo.state.Provider
15982  * Abstract base class for state provider implementations. This class provides methods
15983  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15984  * Provider interface.
15985  */
15986 Roo.state.Provider = function(){
15987     /**
15988      * @event statechange
15989      * Fires when a state change occurs.
15990      * @param {Provider} this This state provider
15991      * @param {String} key The state key which was changed
15992      * @param {String} value The encoded value for the state
15993      */
15994     this.addEvents({
15995         "statechange": true
15996     });
15997     this.state = {};
15998     Roo.state.Provider.superclass.constructor.call(this);
15999 };
16000 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16001     /**
16002      * Returns the current value for a key
16003      * @param {String} name The key name
16004      * @param {Mixed} defaultValue A default value to return if the key's value is not found
16005      * @return {Mixed} The state data
16006      */
16007     get : function(name, defaultValue){
16008         return typeof this.state[name] == "undefined" ?
16009             defaultValue : this.state[name];
16010     },
16011     
16012     /**
16013      * Clears a value from the state
16014      * @param {String} name The key name
16015      */
16016     clear : function(name){
16017         delete this.state[name];
16018         this.fireEvent("statechange", this, name, null);
16019     },
16020     
16021     /**
16022      * Sets the value for a key
16023      * @param {String} name The key name
16024      * @param {Mixed} value The value to set
16025      */
16026     set : function(name, value){
16027         this.state[name] = value;
16028         this.fireEvent("statechange", this, name, value);
16029     },
16030     
16031     /**
16032      * Decodes a string previously encoded with {@link #encodeValue}.
16033      * @param {String} value The value to decode
16034      * @return {Mixed} The decoded value
16035      */
16036     decodeValue : function(cookie){
16037         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16038         var matches = re.exec(unescape(cookie));
16039         if(!matches || !matches[1]) {
16040             return; // non state cookie
16041         }
16042         var type = matches[1];
16043         var v = matches[2];
16044         switch(type){
16045             case "n":
16046                 return parseFloat(v);
16047             case "d":
16048                 return new Date(Date.parse(v));
16049             case "b":
16050                 return (v == "1");
16051             case "a":
16052                 var all = [];
16053                 var values = v.split("^");
16054                 for(var i = 0, len = values.length; i < len; i++){
16055                     all.push(this.decodeValue(values[i]));
16056                 }
16057                 return all;
16058            case "o":
16059                 var all = {};
16060                 var values = v.split("^");
16061                 for(var i = 0, len = values.length; i < len; i++){
16062                     var kv = values[i].split("=");
16063                     all[kv[0]] = this.decodeValue(kv[1]);
16064                 }
16065                 return all;
16066            default:
16067                 return v;
16068         }
16069     },
16070     
16071     /**
16072      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16073      * @param {Mixed} value The value to encode
16074      * @return {String} The encoded value
16075      */
16076     encodeValue : function(v){
16077         var enc;
16078         if(typeof v == "number"){
16079             enc = "n:" + v;
16080         }else if(typeof v == "boolean"){
16081             enc = "b:" + (v ? "1" : "0");
16082         }else if(v instanceof Date){
16083             enc = "d:" + v.toGMTString();
16084         }else if(v instanceof Array){
16085             var flat = "";
16086             for(var i = 0, len = v.length; i < len; i++){
16087                 flat += this.encodeValue(v[i]);
16088                 if(i != len-1) {
16089                     flat += "^";
16090                 }
16091             }
16092             enc = "a:" + flat;
16093         }else if(typeof v == "object"){
16094             var flat = "";
16095             for(var key in v){
16096                 if(typeof v[key] != "function"){
16097                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16098                 }
16099             }
16100             enc = "o:" + flat.substring(0, flat.length-1);
16101         }else{
16102             enc = "s:" + v;
16103         }
16104         return escape(enc);        
16105     }
16106 });
16107
16108 /*
16109  * Based on:
16110  * Ext JS Library 1.1.1
16111  * Copyright(c) 2006-2007, Ext JS, LLC.
16112  *
16113  * Originally Released Under LGPL - original licence link has changed is not relivant.
16114  *
16115  * Fork - LGPL
16116  * <script type="text/javascript">
16117  */
16118 /**
16119  * @class Roo.state.Manager
16120  * This is the global state manager. By default all components that are "state aware" check this class
16121  * for state information if you don't pass them a custom state provider. In order for this class
16122  * to be useful, it must be initialized with a provider when your application initializes.
16123  <pre><code>
16124 // in your initialization function
16125 init : function(){
16126    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16127    ...
16128    // supposed you have a {@link Roo.BorderLayout}
16129    var layout = new Roo.BorderLayout(...);
16130    layout.restoreState();
16131    // or a {Roo.BasicDialog}
16132    var dialog = new Roo.BasicDialog(...);
16133    dialog.restoreState();
16134  </code></pre>
16135  * @static
16136  */
16137 Roo.state.Manager = function(){
16138     var provider = new Roo.state.Provider();
16139     
16140     return {
16141         /**
16142          * Configures the default state provider for your application
16143          * @param {Provider} stateProvider The state provider to set
16144          */
16145         setProvider : function(stateProvider){
16146             provider = stateProvider;
16147         },
16148         
16149         /**
16150          * Returns the current value for a key
16151          * @param {String} name The key name
16152          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16153          * @return {Mixed} The state data
16154          */
16155         get : function(key, defaultValue){
16156             return provider.get(key, defaultValue);
16157         },
16158         
16159         /**
16160          * Sets the value for a key
16161          * @param {String} name The key name
16162          * @param {Mixed} value The state data
16163          */
16164          set : function(key, value){
16165             provider.set(key, value);
16166         },
16167         
16168         /**
16169          * Clears a value from the state
16170          * @param {String} name The key name
16171          */
16172         clear : function(key){
16173             provider.clear(key);
16174         },
16175         
16176         /**
16177          * Gets the currently configured state provider
16178          * @return {Provider} The state provider
16179          */
16180         getProvider : function(){
16181             return provider;
16182         }
16183     };
16184 }();
16185 /*
16186  * Based on:
16187  * Ext JS Library 1.1.1
16188  * Copyright(c) 2006-2007, Ext JS, LLC.
16189  *
16190  * Originally Released Under LGPL - original licence link has changed is not relivant.
16191  *
16192  * Fork - LGPL
16193  * <script type="text/javascript">
16194  */
16195 /**
16196  * @class Roo.state.CookieProvider
16197  * @extends Roo.state.Provider
16198  * The default Provider implementation which saves state via cookies.
16199  * <br />Usage:
16200  <pre><code>
16201    var cp = new Roo.state.CookieProvider({
16202        path: "/cgi-bin/",
16203        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16204        domain: "roojs.com"
16205    })
16206    Roo.state.Manager.setProvider(cp);
16207  </code></pre>
16208  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16209  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16210  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16211  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16212  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16213  * domain the page is running on including the 'www' like 'www.roojs.com')
16214  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16215  * @constructor
16216  * Create a new CookieProvider
16217  * @param {Object} config The configuration object
16218  */
16219 Roo.state.CookieProvider = function(config){
16220     Roo.state.CookieProvider.superclass.constructor.call(this);
16221     this.path = "/";
16222     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16223     this.domain = null;
16224     this.secure = false;
16225     Roo.apply(this, config);
16226     this.state = this.readCookies();
16227 };
16228
16229 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16230     // private
16231     set : function(name, value){
16232         if(typeof value == "undefined" || value === null){
16233             this.clear(name);
16234             return;
16235         }
16236         this.setCookie(name, value);
16237         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16238     },
16239
16240     // private
16241     clear : function(name){
16242         this.clearCookie(name);
16243         Roo.state.CookieProvider.superclass.clear.call(this, name);
16244     },
16245
16246     // private
16247     readCookies : function(){
16248         var cookies = {};
16249         var c = document.cookie + ";";
16250         var re = /\s?(.*?)=(.*?);/g;
16251         var matches;
16252         while((matches = re.exec(c)) != null){
16253             var name = matches[1];
16254             var value = matches[2];
16255             if(name && name.substring(0,3) == "ys-"){
16256                 cookies[name.substr(3)] = this.decodeValue(value);
16257             }
16258         }
16259         return cookies;
16260     },
16261
16262     // private
16263     setCookie : function(name, value){
16264         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16265            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16266            ((this.path == null) ? "" : ("; path=" + this.path)) +
16267            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16268            ((this.secure == true) ? "; secure" : "");
16269     },
16270
16271     // private
16272     clearCookie : function(name){
16273         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16274            ((this.path == null) ? "" : ("; path=" + this.path)) +
16275            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16276            ((this.secure == true) ? "; secure" : "");
16277     }
16278 });/*
16279  * Based on:
16280  * Ext JS Library 1.1.1
16281  * Copyright(c) 2006-2007, Ext JS, LLC.
16282  *
16283  * Originally Released Under LGPL - original licence link has changed is not relivant.
16284  *
16285  * Fork - LGPL
16286  * <script type="text/javascript">
16287  */
16288  
16289
16290 /**
16291  * @class Roo.ComponentMgr
16292  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16293  * @static
16294  */
16295 Roo.ComponentMgr = function(){
16296     var all = new Roo.util.MixedCollection();
16297
16298     return {
16299         /**
16300          * Registers a component.
16301          * @param {Roo.Component} c The component
16302          */
16303         register : function(c){
16304             all.add(c);
16305         },
16306
16307         /**
16308          * Unregisters a component.
16309          * @param {Roo.Component} c The component
16310          */
16311         unregister : function(c){
16312             all.remove(c);
16313         },
16314
16315         /**
16316          * Returns a component by id
16317          * @param {String} id The component id
16318          */
16319         get : function(id){
16320             return all.get(id);
16321         },
16322
16323         /**
16324          * Registers a function that will be called when a specified component is added to ComponentMgr
16325          * @param {String} id The component id
16326          * @param {Funtction} fn The callback function
16327          * @param {Object} scope The scope of the callback
16328          */
16329         onAvailable : function(id, fn, scope){
16330             all.on("add", function(index, o){
16331                 if(o.id == id){
16332                     fn.call(scope || o, o);
16333                     all.un("add", fn, scope);
16334                 }
16335             });
16336         }
16337     };
16338 }();/*
16339  * Based on:
16340  * Ext JS Library 1.1.1
16341  * Copyright(c) 2006-2007, Ext JS, LLC.
16342  *
16343  * Originally Released Under LGPL - original licence link has changed is not relivant.
16344  *
16345  * Fork - LGPL
16346  * <script type="text/javascript">
16347  */
16348  
16349 /**
16350  * @class Roo.Component
16351  * @extends Roo.util.Observable
16352  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16353  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16354  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16355  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16356  * All visual components (widgets) that require rendering into a layout should subclass Component.
16357  * @constructor
16358  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16359  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
16360  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16361  */
16362 Roo.Component = function(config){
16363     config = config || {};
16364     if(config.tagName || config.dom || typeof config == "string"){ // element object
16365         config = {el: config, id: config.id || config};
16366     }
16367     this.initialConfig = config;
16368
16369     Roo.apply(this, config);
16370     this.addEvents({
16371         /**
16372          * @event disable
16373          * Fires after the component is disabled.
16374              * @param {Roo.Component} this
16375              */
16376         disable : true,
16377         /**
16378          * @event enable
16379          * Fires after the component is enabled.
16380              * @param {Roo.Component} this
16381              */
16382         enable : true,
16383         /**
16384          * @event beforeshow
16385          * Fires before the component is shown.  Return false to stop the show.
16386              * @param {Roo.Component} this
16387              */
16388         beforeshow : true,
16389         /**
16390          * @event show
16391          * Fires after the component is shown.
16392              * @param {Roo.Component} this
16393              */
16394         show : true,
16395         /**
16396          * @event beforehide
16397          * Fires before the component is hidden. Return false to stop the hide.
16398              * @param {Roo.Component} this
16399              */
16400         beforehide : true,
16401         /**
16402          * @event hide
16403          * Fires after the component is hidden.
16404              * @param {Roo.Component} this
16405              */
16406         hide : true,
16407         /**
16408          * @event beforerender
16409          * Fires before the component is rendered. Return false to stop the render.
16410              * @param {Roo.Component} this
16411              */
16412         beforerender : true,
16413         /**
16414          * @event render
16415          * Fires after the component is rendered.
16416              * @param {Roo.Component} this
16417              */
16418         render : true,
16419         /**
16420          * @event beforedestroy
16421          * Fires before the component is destroyed. Return false to stop the destroy.
16422              * @param {Roo.Component} this
16423              */
16424         beforedestroy : true,
16425         /**
16426          * @event destroy
16427          * Fires after the component is destroyed.
16428              * @param {Roo.Component} this
16429              */
16430         destroy : true
16431     });
16432     if(!this.id){
16433         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16434     }
16435     Roo.ComponentMgr.register(this);
16436     Roo.Component.superclass.constructor.call(this);
16437     this.initComponent();
16438     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16439         this.render(this.renderTo);
16440         delete this.renderTo;
16441     }
16442 };
16443
16444 /** @private */
16445 Roo.Component.AUTO_ID = 1000;
16446
16447 Roo.extend(Roo.Component, Roo.util.Observable, {
16448     /**
16449      * @scope Roo.Component.prototype
16450      * @type {Boolean}
16451      * true if this component is hidden. Read-only.
16452      */
16453     hidden : false,
16454     /**
16455      * @type {Boolean}
16456      * true if this component is disabled. Read-only.
16457      */
16458     disabled : false,
16459     /**
16460      * @type {Boolean}
16461      * true if this component has been rendered. Read-only.
16462      */
16463     rendered : false,
16464     
16465     /** @cfg {String} disableClass
16466      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16467      */
16468     disabledClass : "x-item-disabled",
16469         /** @cfg {Boolean} allowDomMove
16470          * Whether the component can move the Dom node when rendering (defaults to true).
16471          */
16472     allowDomMove : true,
16473     /** @cfg {String} hideMode (display|visibility)
16474      * How this component should hidden. Supported values are
16475      * "visibility" (css visibility), "offsets" (negative offset position) and
16476      * "display" (css display) - defaults to "display".
16477      */
16478     hideMode: 'display',
16479
16480     /** @private */
16481     ctype : "Roo.Component",
16482
16483     /**
16484      * @cfg {String} actionMode 
16485      * which property holds the element that used for  hide() / show() / disable() / enable()
16486      * default is 'el' for forms you probably want to set this to fieldEl 
16487      */
16488     actionMode : "el",
16489
16490     /** @private */
16491     getActionEl : function(){
16492         return this[this.actionMode];
16493     },
16494
16495     initComponent : Roo.emptyFn,
16496     /**
16497      * If this is a lazy rendering component, render it to its container element.
16498      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
16499      */
16500     render : function(container, position){
16501         
16502         if(this.rendered){
16503             return this;
16504         }
16505         
16506         if(this.fireEvent("beforerender", this) === false){
16507             return false;
16508         }
16509         
16510         if(!container && this.el){
16511             this.el = Roo.get(this.el);
16512             container = this.el.dom.parentNode;
16513             this.allowDomMove = false;
16514         }
16515         this.container = Roo.get(container);
16516         this.rendered = true;
16517         if(position !== undefined){
16518             if(typeof position == 'number'){
16519                 position = this.container.dom.childNodes[position];
16520             }else{
16521                 position = Roo.getDom(position);
16522             }
16523         }
16524         this.onRender(this.container, position || null);
16525         if(this.cls){
16526             this.el.addClass(this.cls);
16527             delete this.cls;
16528         }
16529         if(this.style){
16530             this.el.applyStyles(this.style);
16531             delete this.style;
16532         }
16533         this.fireEvent("render", this);
16534         this.afterRender(this.container);
16535         if(this.hidden){
16536             this.hide();
16537         }
16538         if(this.disabled){
16539             this.disable();
16540         }
16541
16542         return this;
16543         
16544     },
16545
16546     /** @private */
16547     // default function is not really useful
16548     onRender : function(ct, position){
16549         if(this.el){
16550             this.el = Roo.get(this.el);
16551             if(this.allowDomMove !== false){
16552                 ct.dom.insertBefore(this.el.dom, position);
16553             }
16554         }
16555     },
16556
16557     /** @private */
16558     getAutoCreate : function(){
16559         var cfg = typeof this.autoCreate == "object" ?
16560                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16561         if(this.id && !cfg.id){
16562             cfg.id = this.id;
16563         }
16564         return cfg;
16565     },
16566
16567     /** @private */
16568     afterRender : Roo.emptyFn,
16569
16570     /**
16571      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16572      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16573      */
16574     destroy : function(){
16575         if(this.fireEvent("beforedestroy", this) !== false){
16576             this.purgeListeners();
16577             this.beforeDestroy();
16578             if(this.rendered){
16579                 this.el.removeAllListeners();
16580                 this.el.remove();
16581                 if(this.actionMode == "container"){
16582                     this.container.remove();
16583                 }
16584             }
16585             this.onDestroy();
16586             Roo.ComponentMgr.unregister(this);
16587             this.fireEvent("destroy", this);
16588         }
16589     },
16590
16591         /** @private */
16592     beforeDestroy : function(){
16593
16594     },
16595
16596         /** @private */
16597         onDestroy : function(){
16598
16599     },
16600
16601     /**
16602      * Returns the underlying {@link Roo.Element}.
16603      * @return {Roo.Element} The element
16604      */
16605     getEl : function(){
16606         return this.el;
16607     },
16608
16609     /**
16610      * Returns the id of this component.
16611      * @return {String}
16612      */
16613     getId : function(){
16614         return this.id;
16615     },
16616
16617     /**
16618      * Try to focus this component.
16619      * @param {Boolean} selectText True to also select the text in this component (if applicable)
16620      * @return {Roo.Component} this
16621      */
16622     focus : function(selectText){
16623         if(this.rendered){
16624             this.el.focus();
16625             if(selectText === true){
16626                 this.el.dom.select();
16627             }
16628         }
16629         return this;
16630     },
16631
16632     /** @private */
16633     blur : function(){
16634         if(this.rendered){
16635             this.el.blur();
16636         }
16637         return this;
16638     },
16639
16640     /**
16641      * Disable this component.
16642      * @return {Roo.Component} this
16643      */
16644     disable : function(){
16645         if(this.rendered){
16646             this.onDisable();
16647         }
16648         this.disabled = true;
16649         this.fireEvent("disable", this);
16650         return this;
16651     },
16652
16653         // private
16654     onDisable : function(){
16655         this.getActionEl().addClass(this.disabledClass);
16656         this.el.dom.disabled = true;
16657     },
16658
16659     /**
16660      * Enable this component.
16661      * @return {Roo.Component} this
16662      */
16663     enable : function(){
16664         if(this.rendered){
16665             this.onEnable();
16666         }
16667         this.disabled = false;
16668         this.fireEvent("enable", this);
16669         return this;
16670     },
16671
16672         // private
16673     onEnable : function(){
16674         this.getActionEl().removeClass(this.disabledClass);
16675         this.el.dom.disabled = false;
16676     },
16677
16678     /**
16679      * Convenience function for setting disabled/enabled by boolean.
16680      * @param {Boolean} disabled
16681      */
16682     setDisabled : function(disabled){
16683         this[disabled ? "disable" : "enable"]();
16684     },
16685
16686     /**
16687      * Show this component.
16688      * @return {Roo.Component} this
16689      */
16690     show: function(){
16691         if(this.fireEvent("beforeshow", this) !== false){
16692             this.hidden = false;
16693             if(this.rendered){
16694                 this.onShow();
16695             }
16696             this.fireEvent("show", this);
16697         }
16698         return this;
16699     },
16700
16701     // private
16702     onShow : function(){
16703         var ae = this.getActionEl();
16704         if(this.hideMode == 'visibility'){
16705             ae.dom.style.visibility = "visible";
16706         }else if(this.hideMode == 'offsets'){
16707             ae.removeClass('x-hidden');
16708         }else{
16709             ae.dom.style.display = "";
16710         }
16711     },
16712
16713     /**
16714      * Hide this component.
16715      * @return {Roo.Component} this
16716      */
16717     hide: function(){
16718         if(this.fireEvent("beforehide", this) !== false){
16719             this.hidden = true;
16720             if(this.rendered){
16721                 this.onHide();
16722             }
16723             this.fireEvent("hide", this);
16724         }
16725         return this;
16726     },
16727
16728     // private
16729     onHide : function(){
16730         var ae = this.getActionEl();
16731         if(this.hideMode == 'visibility'){
16732             ae.dom.style.visibility = "hidden";
16733         }else if(this.hideMode == 'offsets'){
16734             ae.addClass('x-hidden');
16735         }else{
16736             ae.dom.style.display = "none";
16737         }
16738     },
16739
16740     /**
16741      * Convenience function to hide or show this component by boolean.
16742      * @param {Boolean} visible True to show, false to hide
16743      * @return {Roo.Component} this
16744      */
16745     setVisible: function(visible){
16746         if(visible) {
16747             this.show();
16748         }else{
16749             this.hide();
16750         }
16751         return this;
16752     },
16753
16754     /**
16755      * Returns true if this component is visible.
16756      */
16757     isVisible : function(){
16758         return this.getActionEl().isVisible();
16759     },
16760
16761     cloneConfig : function(overrides){
16762         overrides = overrides || {};
16763         var id = overrides.id || Roo.id();
16764         var cfg = Roo.applyIf(overrides, this.initialConfig);
16765         cfg.id = id; // prevent dup id
16766         return new this.constructor(cfg);
16767     }
16768 });/*
16769  * Based on:
16770  * Ext JS Library 1.1.1
16771  * Copyright(c) 2006-2007, Ext JS, LLC.
16772  *
16773  * Originally Released Under LGPL - original licence link has changed is not relivant.
16774  *
16775  * Fork - LGPL
16776  * <script type="text/javascript">
16777  */
16778
16779 /**
16780  * @class Roo.BoxComponent
16781  * @extends Roo.Component
16782  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
16783  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
16784  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
16785  * layout containers.
16786  * @constructor
16787  * @param {Roo.Element/String/Object} config The configuration options.
16788  */
16789 Roo.BoxComponent = function(config){
16790     Roo.Component.call(this, config);
16791     this.addEvents({
16792         /**
16793          * @event resize
16794          * Fires after the component is resized.
16795              * @param {Roo.Component} this
16796              * @param {Number} adjWidth The box-adjusted width that was set
16797              * @param {Number} adjHeight The box-adjusted height that was set
16798              * @param {Number} rawWidth The width that was originally specified
16799              * @param {Number} rawHeight The height that was originally specified
16800              */
16801         resize : true,
16802         /**
16803          * @event move
16804          * Fires after the component is moved.
16805              * @param {Roo.Component} this
16806              * @param {Number} x The new x position
16807              * @param {Number} y The new y position
16808              */
16809         move : true
16810     });
16811 };
16812
16813 Roo.extend(Roo.BoxComponent, Roo.Component, {
16814     // private, set in afterRender to signify that the component has been rendered
16815     boxReady : false,
16816     // private, used to defer height settings to subclasses
16817     deferHeight: false,
16818     /** @cfg {Number} width
16819      * width (optional) size of component
16820      */
16821      /** @cfg {Number} height
16822      * height (optional) size of component
16823      */
16824      
16825     /**
16826      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
16827      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
16828      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
16829      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
16830      * @return {Roo.BoxComponent} this
16831      */
16832     setSize : function(w, h){
16833         // support for standard size objects
16834         if(typeof w == 'object'){
16835             h = w.height;
16836             w = w.width;
16837         }
16838         // not rendered
16839         if(!this.boxReady){
16840             this.width = w;
16841             this.height = h;
16842             return this;
16843         }
16844
16845         // prevent recalcs when not needed
16846         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
16847             return this;
16848         }
16849         this.lastSize = {width: w, height: h};
16850
16851         var adj = this.adjustSize(w, h);
16852         var aw = adj.width, ah = adj.height;
16853         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16854             var rz = this.getResizeEl();
16855             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16856                 rz.setSize(aw, ah);
16857             }else if(!this.deferHeight && ah !== undefined){
16858                 rz.setHeight(ah);
16859             }else if(aw !== undefined){
16860                 rz.setWidth(aw);
16861             }
16862             this.onResize(aw, ah, w, h);
16863             this.fireEvent('resize', this, aw, ah, w, h);
16864         }
16865         return this;
16866     },
16867
16868     /**
16869      * Gets the current size of the component's underlying element.
16870      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16871      */
16872     getSize : function(){
16873         return this.el.getSize();
16874     },
16875
16876     /**
16877      * Gets the current XY position of the component's underlying element.
16878      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16879      * @return {Array} The XY position of the element (e.g., [100, 200])
16880      */
16881     getPosition : function(local){
16882         if(local === true){
16883             return [this.el.getLeft(true), this.el.getTop(true)];
16884         }
16885         return this.xy || this.el.getXY();
16886     },
16887
16888     /**
16889      * Gets the current box measurements of the component's underlying element.
16890      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16891      * @returns {Object} box An object in the format {x, y, width, height}
16892      */
16893     getBox : function(local){
16894         var s = this.el.getSize();
16895         if(local){
16896             s.x = this.el.getLeft(true);
16897             s.y = this.el.getTop(true);
16898         }else{
16899             var xy = this.xy || this.el.getXY();
16900             s.x = xy[0];
16901             s.y = xy[1];
16902         }
16903         return s;
16904     },
16905
16906     /**
16907      * Sets the current box measurements of the component's underlying element.
16908      * @param {Object} box An object in the format {x, y, width, height}
16909      * @returns {Roo.BoxComponent} this
16910      */
16911     updateBox : function(box){
16912         this.setSize(box.width, box.height);
16913         this.setPagePosition(box.x, box.y);
16914         return this;
16915     },
16916
16917     // protected
16918     getResizeEl : function(){
16919         return this.resizeEl || this.el;
16920     },
16921
16922     // protected
16923     getPositionEl : function(){
16924         return this.positionEl || this.el;
16925     },
16926
16927     /**
16928      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16929      * This method fires the move event.
16930      * @param {Number} left The new left
16931      * @param {Number} top The new top
16932      * @returns {Roo.BoxComponent} this
16933      */
16934     setPosition : function(x, y){
16935         this.x = x;
16936         this.y = y;
16937         if(!this.boxReady){
16938             return this;
16939         }
16940         var adj = this.adjustPosition(x, y);
16941         var ax = adj.x, ay = adj.y;
16942
16943         var el = this.getPositionEl();
16944         if(ax !== undefined || ay !== undefined){
16945             if(ax !== undefined && ay !== undefined){
16946                 el.setLeftTop(ax, ay);
16947             }else if(ax !== undefined){
16948                 el.setLeft(ax);
16949             }else if(ay !== undefined){
16950                 el.setTop(ay);
16951             }
16952             this.onPosition(ax, ay);
16953             this.fireEvent('move', this, ax, ay);
16954         }
16955         return this;
16956     },
16957
16958     /**
16959      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16960      * This method fires the move event.
16961      * @param {Number} x The new x position
16962      * @param {Number} y The new y position
16963      * @returns {Roo.BoxComponent} this
16964      */
16965     setPagePosition : function(x, y){
16966         this.pageX = x;
16967         this.pageY = y;
16968         if(!this.boxReady){
16969             return;
16970         }
16971         if(x === undefined || y === undefined){ // cannot translate undefined points
16972             return;
16973         }
16974         var p = this.el.translatePoints(x, y);
16975         this.setPosition(p.left, p.top);
16976         return this;
16977     },
16978
16979     // private
16980     onRender : function(ct, position){
16981         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16982         if(this.resizeEl){
16983             this.resizeEl = Roo.get(this.resizeEl);
16984         }
16985         if(this.positionEl){
16986             this.positionEl = Roo.get(this.positionEl);
16987         }
16988     },
16989
16990     // private
16991     afterRender : function(){
16992         Roo.BoxComponent.superclass.afterRender.call(this);
16993         this.boxReady = true;
16994         this.setSize(this.width, this.height);
16995         if(this.x || this.y){
16996             this.setPosition(this.x, this.y);
16997         }
16998         if(this.pageX || this.pageY){
16999             this.setPagePosition(this.pageX, this.pageY);
17000         }
17001     },
17002
17003     /**
17004      * Force the component's size to recalculate based on the underlying element's current height and width.
17005      * @returns {Roo.BoxComponent} this
17006      */
17007     syncSize : function(){
17008         delete this.lastSize;
17009         this.setSize(this.el.getWidth(), this.el.getHeight());
17010         return this;
17011     },
17012
17013     /**
17014      * Called after the component is resized, this method is empty by default but can be implemented by any
17015      * subclass that needs to perform custom logic after a resize occurs.
17016      * @param {Number} adjWidth The box-adjusted width that was set
17017      * @param {Number} adjHeight The box-adjusted height that was set
17018      * @param {Number} rawWidth The width that was originally specified
17019      * @param {Number} rawHeight The height that was originally specified
17020      */
17021     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17022
17023     },
17024
17025     /**
17026      * Called after the component is moved, this method is empty by default but can be implemented by any
17027      * subclass that needs to perform custom logic after a move occurs.
17028      * @param {Number} x The new x position
17029      * @param {Number} y The new y position
17030      */
17031     onPosition : function(x, y){
17032
17033     },
17034
17035     // private
17036     adjustSize : function(w, h){
17037         if(this.autoWidth){
17038             w = 'auto';
17039         }
17040         if(this.autoHeight){
17041             h = 'auto';
17042         }
17043         return {width : w, height: h};
17044     },
17045
17046     // private
17047     adjustPosition : function(x, y){
17048         return {x : x, y: y};
17049     }
17050 });/*
17051  * Based on:
17052  * Ext JS Library 1.1.1
17053  * Copyright(c) 2006-2007, Ext JS, LLC.
17054  *
17055  * Originally Released Under LGPL - original licence link has changed is not relivant.
17056  *
17057  * Fork - LGPL
17058  * <script type="text/javascript">
17059  */
17060  (function(){ 
17061 /**
17062  * @class Roo.Layer
17063  * @extends Roo.Element
17064  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17065  * automatic maintaining of shadow/shim positions.
17066  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17067  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17068  * you can pass a string with a CSS class name. False turns off the shadow.
17069  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17070  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17071  * @cfg {String} cls CSS class to add to the element
17072  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17073  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17074  * @constructor
17075  * @param {Object} config An object with config options.
17076  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17077  */
17078
17079 Roo.Layer = function(config, existingEl){
17080     config = config || {};
17081     var dh = Roo.DomHelper;
17082     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17083     if(existingEl){
17084         this.dom = Roo.getDom(existingEl);
17085     }
17086     if(!this.dom){
17087         var o = config.dh || {tag: "div", cls: "x-layer"};
17088         this.dom = dh.append(pel, o);
17089     }
17090     if(config.cls){
17091         this.addClass(config.cls);
17092     }
17093     this.constrain = config.constrain !== false;
17094     this.visibilityMode = Roo.Element.VISIBILITY;
17095     if(config.id){
17096         this.id = this.dom.id = config.id;
17097     }else{
17098         this.id = Roo.id(this.dom);
17099     }
17100     this.zindex = config.zindex || this.getZIndex();
17101     this.position("absolute", this.zindex);
17102     if(config.shadow){
17103         this.shadowOffset = config.shadowOffset || 4;
17104         this.shadow = new Roo.Shadow({
17105             offset : this.shadowOffset,
17106             mode : config.shadow
17107         });
17108     }else{
17109         this.shadowOffset = 0;
17110     }
17111     this.useShim = config.shim !== false && Roo.useShims;
17112     this.useDisplay = config.useDisplay;
17113     this.hide();
17114 };
17115
17116 var supr = Roo.Element.prototype;
17117
17118 // shims are shared among layer to keep from having 100 iframes
17119 var shims = [];
17120
17121 Roo.extend(Roo.Layer, Roo.Element, {
17122
17123     getZIndex : function(){
17124         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17125     },
17126
17127     getShim : function(){
17128         if(!this.useShim){
17129             return null;
17130         }
17131         if(this.shim){
17132             return this.shim;
17133         }
17134         var shim = shims.shift();
17135         if(!shim){
17136             shim = this.createShim();
17137             shim.enableDisplayMode('block');
17138             shim.dom.style.display = 'none';
17139             shim.dom.style.visibility = 'visible';
17140         }
17141         var pn = this.dom.parentNode;
17142         if(shim.dom.parentNode != pn){
17143             pn.insertBefore(shim.dom, this.dom);
17144         }
17145         shim.setStyle('z-index', this.getZIndex()-2);
17146         this.shim = shim;
17147         return shim;
17148     },
17149
17150     hideShim : function(){
17151         if(this.shim){
17152             this.shim.setDisplayed(false);
17153             shims.push(this.shim);
17154             delete this.shim;
17155         }
17156     },
17157
17158     disableShadow : function(){
17159         if(this.shadow){
17160             this.shadowDisabled = true;
17161             this.shadow.hide();
17162             this.lastShadowOffset = this.shadowOffset;
17163             this.shadowOffset = 0;
17164         }
17165     },
17166
17167     enableShadow : function(show){
17168         if(this.shadow){
17169             this.shadowDisabled = false;
17170             this.shadowOffset = this.lastShadowOffset;
17171             delete this.lastShadowOffset;
17172             if(show){
17173                 this.sync(true);
17174             }
17175         }
17176     },
17177
17178     // private
17179     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17180     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17181     sync : function(doShow){
17182         var sw = this.shadow;
17183         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17184             var sh = this.getShim();
17185
17186             var w = this.getWidth(),
17187                 h = this.getHeight();
17188
17189             var l = this.getLeft(true),
17190                 t = this.getTop(true);
17191
17192             if(sw && !this.shadowDisabled){
17193                 if(doShow && !sw.isVisible()){
17194                     sw.show(this);
17195                 }else{
17196                     sw.realign(l, t, w, h);
17197                 }
17198                 if(sh){
17199                     if(doShow){
17200                        sh.show();
17201                     }
17202                     // fit the shim behind the shadow, so it is shimmed too
17203                     var a = sw.adjusts, s = sh.dom.style;
17204                     s.left = (Math.min(l, l+a.l))+"px";
17205                     s.top = (Math.min(t, t+a.t))+"px";
17206                     s.width = (w+a.w)+"px";
17207                     s.height = (h+a.h)+"px";
17208                 }
17209             }else if(sh){
17210                 if(doShow){
17211                    sh.show();
17212                 }
17213                 sh.setSize(w, h);
17214                 sh.setLeftTop(l, t);
17215             }
17216             
17217         }
17218     },
17219
17220     // private
17221     destroy : function(){
17222         this.hideShim();
17223         if(this.shadow){
17224             this.shadow.hide();
17225         }
17226         this.removeAllListeners();
17227         var pn = this.dom.parentNode;
17228         if(pn){
17229             pn.removeChild(this.dom);
17230         }
17231         Roo.Element.uncache(this.id);
17232     },
17233
17234     remove : function(){
17235         this.destroy();
17236     },
17237
17238     // private
17239     beginUpdate : function(){
17240         this.updating = true;
17241     },
17242
17243     // private
17244     endUpdate : function(){
17245         this.updating = false;
17246         this.sync(true);
17247     },
17248
17249     // private
17250     hideUnders : function(negOffset){
17251         if(this.shadow){
17252             this.shadow.hide();
17253         }
17254         this.hideShim();
17255     },
17256
17257     // private
17258     constrainXY : function(){
17259         if(this.constrain){
17260             var vw = Roo.lib.Dom.getViewWidth(),
17261                 vh = Roo.lib.Dom.getViewHeight();
17262             var s = Roo.get(document).getScroll();
17263
17264             var xy = this.getXY();
17265             var x = xy[0], y = xy[1];   
17266             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17267             // only move it if it needs it
17268             var moved = false;
17269             // first validate right/bottom
17270             if((x + w) > vw+s.left){
17271                 x = vw - w - this.shadowOffset;
17272                 moved = true;
17273             }
17274             if((y + h) > vh+s.top){
17275                 y = vh - h - this.shadowOffset;
17276                 moved = true;
17277             }
17278             // then make sure top/left isn't negative
17279             if(x < s.left){
17280                 x = s.left;
17281                 moved = true;
17282             }
17283             if(y < s.top){
17284                 y = s.top;
17285                 moved = true;
17286             }
17287             if(moved){
17288                 if(this.avoidY){
17289                     var ay = this.avoidY;
17290                     if(y <= ay && (y+h) >= ay){
17291                         y = ay-h-5;   
17292                     }
17293                 }
17294                 xy = [x, y];
17295                 this.storeXY(xy);
17296                 supr.setXY.call(this, xy);
17297                 this.sync();
17298             }
17299         }
17300     },
17301
17302     isVisible : function(){
17303         return this.visible;    
17304     },
17305
17306     // private
17307     showAction : function(){
17308         this.visible = true; // track visibility to prevent getStyle calls
17309         if(this.useDisplay === true){
17310             this.setDisplayed("");
17311         }else if(this.lastXY){
17312             supr.setXY.call(this, this.lastXY);
17313         }else if(this.lastLT){
17314             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17315         }
17316     },
17317
17318     // private
17319     hideAction : function(){
17320         this.visible = false;
17321         if(this.useDisplay === true){
17322             this.setDisplayed(false);
17323         }else{
17324             this.setLeftTop(-10000,-10000);
17325         }
17326     },
17327
17328     // overridden Element method
17329     setVisible : function(v, a, d, c, e){
17330         if(v){
17331             this.showAction();
17332         }
17333         if(a && v){
17334             var cb = function(){
17335                 this.sync(true);
17336                 if(c){
17337                     c();
17338                 }
17339             }.createDelegate(this);
17340             supr.setVisible.call(this, true, true, d, cb, e);
17341         }else{
17342             if(!v){
17343                 this.hideUnders(true);
17344             }
17345             var cb = c;
17346             if(a){
17347                 cb = function(){
17348                     this.hideAction();
17349                     if(c){
17350                         c();
17351                     }
17352                 }.createDelegate(this);
17353             }
17354             supr.setVisible.call(this, v, a, d, cb, e);
17355             if(v){
17356                 this.sync(true);
17357             }else if(!a){
17358                 this.hideAction();
17359             }
17360         }
17361     },
17362
17363     storeXY : function(xy){
17364         delete this.lastLT;
17365         this.lastXY = xy;
17366     },
17367
17368     storeLeftTop : function(left, top){
17369         delete this.lastXY;
17370         this.lastLT = [left, top];
17371     },
17372
17373     // private
17374     beforeFx : function(){
17375         this.beforeAction();
17376         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17377     },
17378
17379     // private
17380     afterFx : function(){
17381         Roo.Layer.superclass.afterFx.apply(this, arguments);
17382         this.sync(this.isVisible());
17383     },
17384
17385     // private
17386     beforeAction : function(){
17387         if(!this.updating && this.shadow){
17388             this.shadow.hide();
17389         }
17390     },
17391
17392     // overridden Element method
17393     setLeft : function(left){
17394         this.storeLeftTop(left, this.getTop(true));
17395         supr.setLeft.apply(this, arguments);
17396         this.sync();
17397     },
17398
17399     setTop : function(top){
17400         this.storeLeftTop(this.getLeft(true), top);
17401         supr.setTop.apply(this, arguments);
17402         this.sync();
17403     },
17404
17405     setLeftTop : function(left, top){
17406         this.storeLeftTop(left, top);
17407         supr.setLeftTop.apply(this, arguments);
17408         this.sync();
17409     },
17410
17411     setXY : function(xy, a, d, c, e){
17412         this.fixDisplay();
17413         this.beforeAction();
17414         this.storeXY(xy);
17415         var cb = this.createCB(c);
17416         supr.setXY.call(this, xy, a, d, cb, e);
17417         if(!a){
17418             cb();
17419         }
17420     },
17421
17422     // private
17423     createCB : function(c){
17424         var el = this;
17425         return function(){
17426             el.constrainXY();
17427             el.sync(true);
17428             if(c){
17429                 c();
17430             }
17431         };
17432     },
17433
17434     // overridden Element method
17435     setX : function(x, a, d, c, e){
17436         this.setXY([x, this.getY()], a, d, c, e);
17437     },
17438
17439     // overridden Element method
17440     setY : function(y, a, d, c, e){
17441         this.setXY([this.getX(), y], a, d, c, e);
17442     },
17443
17444     // overridden Element method
17445     setSize : function(w, h, a, d, c, e){
17446         this.beforeAction();
17447         var cb = this.createCB(c);
17448         supr.setSize.call(this, w, h, a, d, cb, e);
17449         if(!a){
17450             cb();
17451         }
17452     },
17453
17454     // overridden Element method
17455     setWidth : function(w, a, d, c, e){
17456         this.beforeAction();
17457         var cb = this.createCB(c);
17458         supr.setWidth.call(this, w, a, d, cb, e);
17459         if(!a){
17460             cb();
17461         }
17462     },
17463
17464     // overridden Element method
17465     setHeight : function(h, a, d, c, e){
17466         this.beforeAction();
17467         var cb = this.createCB(c);
17468         supr.setHeight.call(this, h, a, d, cb, e);
17469         if(!a){
17470             cb();
17471         }
17472     },
17473
17474     // overridden Element method
17475     setBounds : function(x, y, w, h, a, d, c, e){
17476         this.beforeAction();
17477         var cb = this.createCB(c);
17478         if(!a){
17479             this.storeXY([x, y]);
17480             supr.setXY.call(this, [x, y]);
17481             supr.setSize.call(this, w, h, a, d, cb, e);
17482             cb();
17483         }else{
17484             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17485         }
17486         return this;
17487     },
17488     
17489     /**
17490      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17491      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17492      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17493      * @param {Number} zindex The new z-index to set
17494      * @return {this} The Layer
17495      */
17496     setZIndex : function(zindex){
17497         this.zindex = zindex;
17498         this.setStyle("z-index", zindex + 2);
17499         if(this.shadow){
17500             this.shadow.setZIndex(zindex + 1);
17501         }
17502         if(this.shim){
17503             this.shim.setStyle("z-index", zindex);
17504         }
17505     }
17506 });
17507 })();/*
17508  * Original code for Roojs - LGPL
17509  * <script type="text/javascript">
17510  */
17511  
17512 /**
17513  * @class Roo.XComponent
17514  * A delayed Element creator...
17515  * Or a way to group chunks of interface together.
17516  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17517  *  used in conjunction with XComponent.build() it will create an instance of each element,
17518  *  then call addxtype() to build the User interface.
17519  * 
17520  * Mypart.xyx = new Roo.XComponent({
17521
17522     parent : 'Mypart.xyz', // empty == document.element.!!
17523     order : '001',
17524     name : 'xxxx'
17525     region : 'xxxx'
17526     disabled : function() {} 
17527      
17528     tree : function() { // return an tree of xtype declared components
17529         var MODULE = this;
17530         return 
17531         {
17532             xtype : 'NestedLayoutPanel',
17533             // technicall
17534         }
17535      ]
17536  *})
17537  *
17538  *
17539  * It can be used to build a big heiracy, with parent etc.
17540  * or you can just use this to render a single compoent to a dom element
17541  * MYPART.render(Roo.Element | String(id) | dom_element )
17542  *
17543  *
17544  * Usage patterns.
17545  *
17546  * Classic Roo
17547  *
17548  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17549  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17550  *
17551  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17552  *
17553  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17554  * - if mulitple topModules exist, the last one is defined as the top module.
17555  *
17556  * Embeded Roo
17557  * 
17558  * When the top level or multiple modules are to embedded into a existing HTML page,
17559  * the parent element can container '#id' of the element where the module will be drawn.
17560  *
17561  * Bootstrap Roo
17562  *
17563  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17564  * it relies more on a include mechanism, where sub modules are included into an outer page.
17565  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17566  * 
17567  * Bootstrap Roo Included elements
17568  *
17569  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17570  * hence confusing the component builder as it thinks there are multiple top level elements. 
17571  *
17572  * String Over-ride & Translations
17573  *
17574  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17575  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17576  * are needed. @see Roo.XComponent.overlayString  
17577  * 
17578  * 
17579  * 
17580  * @extends Roo.util.Observable
17581  * @constructor
17582  * @param cfg {Object} configuration of component
17583  * 
17584  */
17585 Roo.XComponent = function(cfg) {
17586     Roo.apply(this, cfg);
17587     this.addEvents({ 
17588         /**
17589              * @event built
17590              * Fires when this the componnt is built
17591              * @param {Roo.XComponent} c the component
17592              */
17593         'built' : true
17594         
17595     });
17596     this.region = this.region || 'center'; // default..
17597     Roo.XComponent.register(this);
17598     this.modules = false;
17599     this.el = false; // where the layout goes..
17600     
17601     
17602 }
17603 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17604     /**
17605      * @property el
17606      * The created element (with Roo.factory())
17607      * @type {Roo.Layout}
17608      */
17609     el  : false,
17610     
17611     /**
17612      * @property el
17613      * for BC  - use el in new code
17614      * @type {Roo.Layout}
17615      */
17616     panel : false,
17617     
17618     /**
17619      * @property layout
17620      * for BC  - use el in new code
17621      * @type {Roo.Layout}
17622      */
17623     layout : false,
17624     
17625      /**
17626      * @cfg {Function|boolean} disabled
17627      * If this module is disabled by some rule, return true from the funtion
17628      */
17629     disabled : false,
17630     
17631     /**
17632      * @cfg {String} parent 
17633      * Name of parent element which it get xtype added to..
17634      */
17635     parent: false,
17636     
17637     /**
17638      * @cfg {String} order
17639      * Used to set the order in which elements are created (usefull for multiple tabs)
17640      */
17641     
17642     order : false,
17643     /**
17644      * @cfg {String} name
17645      * String to display while loading.
17646      */
17647     name : false,
17648     /**
17649      * @cfg {String} region
17650      * Region to render component to (defaults to center)
17651      */
17652     region : 'center',
17653     
17654     /**
17655      * @cfg {Array} items
17656      * A single item array - the first element is the root of the tree..
17657      * It's done this way to stay compatible with the Xtype system...
17658      */
17659     items : false,
17660     
17661     /**
17662      * @property _tree
17663      * The method that retuns the tree of parts that make up this compoennt 
17664      * @type {function}
17665      */
17666     _tree  : false,
17667     
17668      /**
17669      * render
17670      * render element to dom or tree
17671      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17672      */
17673     
17674     render : function(el)
17675     {
17676         
17677         el = el || false;
17678         var hp = this.parent ? 1 : 0;
17679         Roo.debug &&  Roo.log(this);
17680         
17681         var tree = this._tree ? this._tree() : this.tree();
17682
17683         
17684         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
17685             // if parent is a '#.....' string, then let's use that..
17686             var ename = this.parent.substr(1);
17687             this.parent = false;
17688             Roo.debug && Roo.log(ename);
17689             switch (ename) {
17690                 case 'bootstrap-body':
17691                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
17692                         // this is the BorderLayout standard?
17693                        this.parent = { el : true };
17694                        break;
17695                     }
17696                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
17697                         // need to insert stuff...
17698                         this.parent =  {
17699                              el : new Roo.bootstrap.layout.Border({
17700                                  el : document.body, 
17701                      
17702                                  center: {
17703                                     titlebar: false,
17704                                     autoScroll:false,
17705                                     closeOnTab: true,
17706                                     tabPosition: 'top',
17707                                       //resizeTabs: true,
17708                                     alwaysShowTabs: true,
17709                                     hideTabs: false
17710                                      //minTabWidth: 140
17711                                  }
17712                              })
17713                         
17714                          };
17715                          break;
17716                     }
17717                          
17718                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
17719                         this.parent = { el :  new  Roo.bootstrap.Body() };
17720                         Roo.debug && Roo.log("setting el to doc body");
17721                          
17722                     } else {
17723                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
17724                     }
17725                     break;
17726                 case 'bootstrap':
17727                     this.parent = { el : true};
17728                     // fall through
17729                 default:
17730                     el = Roo.get(ename);
17731                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
17732                         this.parent = { el : true};
17733                     }
17734                     
17735                     break;
17736             }
17737                 
17738             
17739             if (!el && !this.parent) {
17740                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
17741                 return;
17742             }
17743         }
17744         
17745         Roo.debug && Roo.log("EL:");
17746         Roo.debug && Roo.log(el);
17747         Roo.debug && Roo.log("this.parent.el:");
17748         Roo.debug && Roo.log(this.parent.el);
17749         
17750
17751         // altertive root elements ??? - we need a better way to indicate these.
17752         var is_alt = Roo.XComponent.is_alt ||
17753                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
17754                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
17755                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
17756         
17757         
17758         
17759         if (!this.parent && is_alt) {
17760             //el = Roo.get(document.body);
17761             this.parent = { el : true };
17762         }
17763             
17764             
17765         
17766         if (!this.parent) {
17767             
17768             Roo.debug && Roo.log("no parent - creating one");
17769             
17770             el = el ? Roo.get(el) : false;      
17771             
17772             if (typeof(Roo.BorderLayout) == 'undefined' ) {
17773                 
17774                 this.parent =  {
17775                     el : new Roo.bootstrap.layout.Border({
17776                         el: el || document.body,
17777                     
17778                         center: {
17779                             titlebar: false,
17780                             autoScroll:false,
17781                             closeOnTab: true,
17782                             tabPosition: 'top',
17783                              //resizeTabs: true,
17784                             alwaysShowTabs: false,
17785                             hideTabs: true,
17786                             minTabWidth: 140,
17787                             overflow: 'visible'
17788                          }
17789                      })
17790                 };
17791             } else {
17792             
17793                 // it's a top level one..
17794                 this.parent =  {
17795                     el : new Roo.BorderLayout(el || document.body, {
17796                         center: {
17797                             titlebar: false,
17798                             autoScroll:false,
17799                             closeOnTab: true,
17800                             tabPosition: 'top',
17801                              //resizeTabs: true,
17802                             alwaysShowTabs: el && hp? false :  true,
17803                             hideTabs: el || !hp ? true :  false,
17804                             minTabWidth: 140
17805                          }
17806                     })
17807                 };
17808             }
17809         }
17810         
17811         if (!this.parent.el) {
17812                 // probably an old style ctor, which has been disabled.
17813                 return;
17814
17815         }
17816                 // The 'tree' method is  '_tree now' 
17817             
17818         tree.region = tree.region || this.region;
17819         var is_body = false;
17820         if (this.parent.el === true) {
17821             // bootstrap... - body..
17822             if (el) {
17823                 tree.el = el;
17824             }
17825             this.parent.el = Roo.factory(tree);
17826             is_body = true;
17827         }
17828         
17829         this.el = this.parent.el.addxtype(tree, undefined, is_body);
17830         this.fireEvent('built', this);
17831         
17832         this.panel = this.el;
17833         this.layout = this.panel.layout;
17834         this.parentLayout = this.parent.layout  || false;  
17835          
17836     }
17837     
17838 });
17839
17840 Roo.apply(Roo.XComponent, {
17841     /**
17842      * @property  hideProgress
17843      * true to disable the building progress bar.. usefull on single page renders.
17844      * @type Boolean
17845      */
17846     hideProgress : false,
17847     /**
17848      * @property  buildCompleted
17849      * True when the builder has completed building the interface.
17850      * @type Boolean
17851      */
17852     buildCompleted : false,
17853      
17854     /**
17855      * @property  topModule
17856      * the upper most module - uses document.element as it's constructor.
17857      * @type Object
17858      */
17859      
17860     topModule  : false,
17861       
17862     /**
17863      * @property  modules
17864      * array of modules to be created by registration system.
17865      * @type {Array} of Roo.XComponent
17866      */
17867     
17868     modules : [],
17869     /**
17870      * @property  elmodules
17871      * array of modules to be created by which use #ID 
17872      * @type {Array} of Roo.XComponent
17873      */
17874      
17875     elmodules : [],
17876
17877      /**
17878      * @property  is_alt
17879      * Is an alternative Root - normally used by bootstrap or other systems,
17880      *    where the top element in the tree can wrap 'body' 
17881      * @type {boolean}  (default false)
17882      */
17883      
17884     is_alt : false,
17885     /**
17886      * @property  build_from_html
17887      * Build elements from html - used by bootstrap HTML stuff 
17888      *    - this is cleared after build is completed
17889      * @type {boolean}    (default false)
17890      */
17891      
17892     build_from_html : false,
17893     /**
17894      * Register components to be built later.
17895      *
17896      * This solves the following issues
17897      * - Building is not done on page load, but after an authentication process has occured.
17898      * - Interface elements are registered on page load
17899      * - Parent Interface elements may not be loaded before child, so this handles that..
17900      * 
17901      *
17902      * example:
17903      * 
17904      * MyApp.register({
17905           order : '000001',
17906           module : 'Pman.Tab.projectMgr',
17907           region : 'center',
17908           parent : 'Pman.layout',
17909           disabled : false,  // or use a function..
17910         })
17911      
17912      * * @param {Object} details about module
17913      */
17914     register : function(obj) {
17915                 
17916         Roo.XComponent.event.fireEvent('register', obj);
17917         switch(typeof(obj.disabled) ) {
17918                 
17919             case 'undefined':
17920                 break;
17921             
17922             case 'function':
17923                 if ( obj.disabled() ) {
17924                         return;
17925                 }
17926                 break;
17927             
17928             default:
17929                 if (obj.disabled || obj.region == '#disabled') {
17930                         return;
17931                 }
17932                 break;
17933         }
17934                 
17935         this.modules.push(obj);
17936          
17937     },
17938     /**
17939      * convert a string to an object..
17940      * eg. 'AAA.BBB' -> finds AAA.BBB
17941
17942      */
17943     
17944     toObject : function(str)
17945     {
17946         if (!str || typeof(str) == 'object') {
17947             return str;
17948         }
17949         if (str.substring(0,1) == '#') {
17950             return str;
17951         }
17952
17953         var ar = str.split('.');
17954         var rt, o;
17955         rt = ar.shift();
17956             /** eval:var:o */
17957         try {
17958             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17959         } catch (e) {
17960             throw "Module not found : " + str;
17961         }
17962         
17963         if (o === false) {
17964             throw "Module not found : " + str;
17965         }
17966         Roo.each(ar, function(e) {
17967             if (typeof(o[e]) == 'undefined') {
17968                 throw "Module not found : " + str;
17969             }
17970             o = o[e];
17971         });
17972         
17973         return o;
17974         
17975     },
17976     
17977     
17978     /**
17979      * move modules into their correct place in the tree..
17980      * 
17981      */
17982     preBuild : function ()
17983     {
17984         var _t = this;
17985         Roo.each(this.modules , function (obj)
17986         {
17987             Roo.XComponent.event.fireEvent('beforebuild', obj);
17988             
17989             var opar = obj.parent;
17990             try { 
17991                 obj.parent = this.toObject(opar);
17992             } catch(e) {
17993                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17994                 return;
17995             }
17996             
17997             if (!obj.parent) {
17998                 Roo.debug && Roo.log("GOT top level module");
17999                 Roo.debug && Roo.log(obj);
18000                 obj.modules = new Roo.util.MixedCollection(false, 
18001                     function(o) { return o.order + '' }
18002                 );
18003                 this.topModule = obj;
18004                 return;
18005             }
18006                         // parent is a string (usually a dom element name..)
18007             if (typeof(obj.parent) == 'string') {
18008                 this.elmodules.push(obj);
18009                 return;
18010             }
18011             if (obj.parent.constructor != Roo.XComponent) {
18012                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18013             }
18014             if (!obj.parent.modules) {
18015                 obj.parent.modules = new Roo.util.MixedCollection(false, 
18016                     function(o) { return o.order + '' }
18017                 );
18018             }
18019             if (obj.parent.disabled) {
18020                 obj.disabled = true;
18021             }
18022             obj.parent.modules.add(obj);
18023         }, this);
18024     },
18025     
18026      /**
18027      * make a list of modules to build.
18028      * @return {Array} list of modules. 
18029      */ 
18030     
18031     buildOrder : function()
18032     {
18033         var _this = this;
18034         var cmp = function(a,b) {   
18035             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18036         };
18037         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18038             throw "No top level modules to build";
18039         }
18040         
18041         // make a flat list in order of modules to build.
18042         var mods = this.topModule ? [ this.topModule ] : [];
18043                 
18044         
18045         // elmodules (is a list of DOM based modules )
18046         Roo.each(this.elmodules, function(e) {
18047             mods.push(e);
18048             if (!this.topModule &&
18049                 typeof(e.parent) == 'string' &&
18050                 e.parent.substring(0,1) == '#' &&
18051                 Roo.get(e.parent.substr(1))
18052                ) {
18053                 
18054                 _this.topModule = e;
18055             }
18056             
18057         });
18058
18059         
18060         // add modules to their parents..
18061         var addMod = function(m) {
18062             Roo.debug && Roo.log("build Order: add: " + m.name);
18063                 
18064             mods.push(m);
18065             if (m.modules && !m.disabled) {
18066                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18067                 m.modules.keySort('ASC',  cmp );
18068                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18069     
18070                 m.modules.each(addMod);
18071             } else {
18072                 Roo.debug && Roo.log("build Order: no child modules");
18073             }
18074             // not sure if this is used any more..
18075             if (m.finalize) {
18076                 m.finalize.name = m.name + " (clean up) ";
18077                 mods.push(m.finalize);
18078             }
18079             
18080         }
18081         if (this.topModule && this.topModule.modules) { 
18082             this.topModule.modules.keySort('ASC',  cmp );
18083             this.topModule.modules.each(addMod);
18084         } 
18085         return mods;
18086     },
18087     
18088      /**
18089      * Build the registered modules.
18090      * @param {Object} parent element.
18091      * @param {Function} optional method to call after module has been added.
18092      * 
18093      */ 
18094    
18095     build : function(opts) 
18096     {
18097         
18098         if (typeof(opts) != 'undefined') {
18099             Roo.apply(this,opts);
18100         }
18101         
18102         this.preBuild();
18103         var mods = this.buildOrder();
18104       
18105         //this.allmods = mods;
18106         //Roo.debug && Roo.log(mods);
18107         //return;
18108         if (!mods.length) { // should not happen
18109             throw "NO modules!!!";
18110         }
18111         
18112         
18113         var msg = "Building Interface...";
18114         // flash it up as modal - so we store the mask!?
18115         if (!this.hideProgress && Roo.MessageBox) {
18116             Roo.MessageBox.show({ title: 'loading' });
18117             Roo.MessageBox.show({
18118                title: "Please wait...",
18119                msg: msg,
18120                width:450,
18121                progress:true,
18122                buttons : false,
18123                closable:false,
18124                modal: false
18125               
18126             });
18127         }
18128         var total = mods.length;
18129         
18130         var _this = this;
18131         var progressRun = function() {
18132             if (!mods.length) {
18133                 Roo.debug && Roo.log('hide?');
18134                 if (!this.hideProgress && Roo.MessageBox) {
18135                     Roo.MessageBox.hide();
18136                 }
18137                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18138                 
18139                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18140                 
18141                 // THE END...
18142                 return false;   
18143             }
18144             
18145             var m = mods.shift();
18146             
18147             
18148             Roo.debug && Roo.log(m);
18149             // not sure if this is supported any more.. - modules that are are just function
18150             if (typeof(m) == 'function') { 
18151                 m.call(this);
18152                 return progressRun.defer(10, _this);
18153             } 
18154             
18155             
18156             msg = "Building Interface " + (total  - mods.length) + 
18157                     " of " + total + 
18158                     (m.name ? (' - ' + m.name) : '');
18159                         Roo.debug && Roo.log(msg);
18160             if (!_this.hideProgress &&  Roo.MessageBox) { 
18161                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18162             }
18163             
18164          
18165             // is the module disabled?
18166             var disabled = (typeof(m.disabled) == 'function') ?
18167                 m.disabled.call(m.module.disabled) : m.disabled;    
18168             
18169             
18170             if (disabled) {
18171                 return progressRun(); // we do not update the display!
18172             }
18173             
18174             // now build 
18175             
18176                         
18177                         
18178             m.render();
18179             // it's 10 on top level, and 1 on others??? why...
18180             return progressRun.defer(10, _this);
18181              
18182         }
18183         progressRun.defer(1, _this);
18184      
18185         
18186         
18187     },
18188     /**
18189      * Overlay a set of modified strings onto a component
18190      * This is dependant on our builder exporting the strings and 'named strings' elements.
18191      * 
18192      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18193      * @param {Object} associative array of 'named' string and it's new value.
18194      * 
18195      */
18196         overlayStrings : function( component, strings )
18197     {
18198         if (typeof(component['_named_strings']) == 'undefined') {
18199             throw "ERROR: component does not have _named_strings";
18200         }
18201         for ( var k in strings ) {
18202             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18203             if (md !== false) {
18204                 component['_strings'][md] = strings[k];
18205             } else {
18206                 Roo.log('could not find named string: ' + k + ' in');
18207                 Roo.log(component);
18208             }
18209             
18210         }
18211         
18212     },
18213     
18214         
18215         /**
18216          * Event Object.
18217          *
18218          *
18219          */
18220         event: false, 
18221     /**
18222          * wrapper for event.on - aliased later..  
18223          * Typically use to register a event handler for register:
18224          *
18225          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18226          *
18227          */
18228     on : false
18229    
18230     
18231     
18232 });
18233
18234 Roo.XComponent.event = new Roo.util.Observable({
18235                 events : { 
18236                         /**
18237                          * @event register
18238                          * Fires when an Component is registered,
18239                          * set the disable property on the Component to stop registration.
18240                          * @param {Roo.XComponent} c the component being registerd.
18241                          * 
18242                          */
18243                         'register' : true,
18244             /**
18245                          * @event beforebuild
18246                          * Fires before each Component is built
18247                          * can be used to apply permissions.
18248                          * @param {Roo.XComponent} c the component being registerd.
18249                          * 
18250                          */
18251                         'beforebuild' : true,
18252                         /**
18253                          * @event buildcomplete
18254                          * Fires on the top level element when all elements have been built
18255                          * @param {Roo.XComponent} the top level component.
18256                          */
18257                         'buildcomplete' : true
18258                         
18259                 }
18260 });
18261
18262 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18263  //
18264  /**
18265  * marked - a markdown parser
18266  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18267  * https://github.com/chjj/marked
18268  */
18269
18270
18271 /**
18272  *
18273  * Roo.Markdown - is a very crude wrapper around marked..
18274  *
18275  * usage:
18276  * 
18277  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18278  * 
18279  * Note: move the sample code to the bottom of this
18280  * file before uncommenting it.
18281  *
18282  */
18283
18284 Roo.Markdown = {};
18285 Roo.Markdown.toHtml = function(text) {
18286     
18287     var c = new Roo.Markdown.marked.setOptions({
18288             renderer: new Roo.Markdown.marked.Renderer(),
18289             gfm: true,
18290             tables: true,
18291             breaks: false,
18292             pedantic: false,
18293             sanitize: false,
18294             smartLists: true,
18295             smartypants: false
18296           });
18297     // A FEW HACKS!!?
18298     
18299     text = text.replace(/\\\n/g,' ');
18300     return Roo.Markdown.marked(text);
18301 };
18302 //
18303 // converter
18304 //
18305 // Wraps all "globals" so that the only thing
18306 // exposed is makeHtml().
18307 //
18308 (function() {
18309     
18310      /**
18311          * eval:var:escape
18312          * eval:var:unescape
18313          * eval:var:replace
18314          */
18315       
18316     /**
18317      * Helpers
18318      */
18319     
18320     var escape = function (html, encode) {
18321       return html
18322         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18323         .replace(/</g, '&lt;')
18324         .replace(/>/g, '&gt;')
18325         .replace(/"/g, '&quot;')
18326         .replace(/'/g, '&#39;');
18327     }
18328     
18329     var unescape = function (html) {
18330         // explicitly match decimal, hex, and named HTML entities 
18331       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18332         n = n.toLowerCase();
18333         if (n === 'colon') { return ':'; }
18334         if (n.charAt(0) === '#') {
18335           return n.charAt(1) === 'x'
18336             ? String.fromCharCode(parseInt(n.substring(2), 16))
18337             : String.fromCharCode(+n.substring(1));
18338         }
18339         return '';
18340       });
18341     }
18342     
18343     var replace = function (regex, opt) {
18344       regex = regex.source;
18345       opt = opt || '';
18346       return function self(name, val) {
18347         if (!name) { return new RegExp(regex, opt); }
18348         val = val.source || val;
18349         val = val.replace(/(^|[^\[])\^/g, '$1');
18350         regex = regex.replace(name, val);
18351         return self;
18352       };
18353     }
18354
18355
18356          /**
18357          * eval:var:noop
18358     */
18359     var noop = function () {}
18360     noop.exec = noop;
18361     
18362          /**
18363          * eval:var:merge
18364     */
18365     var merge = function (obj) {
18366       var i = 1
18367         , target
18368         , key;
18369     
18370       for (; i < arguments.length; i++) {
18371         target = arguments[i];
18372         for (key in target) {
18373           if (Object.prototype.hasOwnProperty.call(target, key)) {
18374             obj[key] = target[key];
18375           }
18376         }
18377       }
18378     
18379       return obj;
18380     }
18381     
18382     
18383     /**
18384      * Block-Level Grammar
18385      */
18386     
18387     
18388     
18389     
18390     var block = {
18391       newline: /^\n+/,
18392       code: /^( {4}[^\n]+\n*)+/,
18393       fences: noop,
18394       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18395       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18396       nptable: noop,
18397       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18398       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18399       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18400       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18401       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18402       table: noop,
18403       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18404       text: /^[^\n]+/
18405     };
18406     
18407     block.bullet = /(?:[*+-]|\d+\.)/;
18408     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18409     block.item = replace(block.item, 'gm')
18410       (/bull/g, block.bullet)
18411       ();
18412     
18413     block.list = replace(block.list)
18414       (/bull/g, block.bullet)
18415       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18416       ('def', '\\n+(?=' + block.def.source + ')')
18417       ();
18418     
18419     block.blockquote = replace(block.blockquote)
18420       ('def', block.def)
18421       ();
18422     
18423     block._tag = '(?!(?:'
18424       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18425       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18426       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18427     
18428     block.html = replace(block.html)
18429       ('comment', /<!--[\s\S]*?-->/)
18430       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18431       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18432       (/tag/g, block._tag)
18433       ();
18434     
18435     block.paragraph = replace(block.paragraph)
18436       ('hr', block.hr)
18437       ('heading', block.heading)
18438       ('lheading', block.lheading)
18439       ('blockquote', block.blockquote)
18440       ('tag', '<' + block._tag)
18441       ('def', block.def)
18442       ();
18443     
18444     /**
18445      * Normal Block Grammar
18446      */
18447     
18448     block.normal = merge({}, block);
18449     
18450     /**
18451      * GFM Block Grammar
18452      */
18453     
18454     block.gfm = merge({}, block.normal, {
18455       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18456       paragraph: /^/,
18457       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18458     });
18459     
18460     block.gfm.paragraph = replace(block.paragraph)
18461       ('(?!', '(?!'
18462         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18463         + block.list.source.replace('\\1', '\\3') + '|')
18464       ();
18465     
18466     /**
18467      * GFM + Tables Block Grammar
18468      */
18469     
18470     block.tables = merge({}, block.gfm, {
18471       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18472       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18473     });
18474     
18475     /**
18476      * Block Lexer
18477      */
18478     
18479     var Lexer = function (options) {
18480       this.tokens = [];
18481       this.tokens.links = {};
18482       this.options = options || marked.defaults;
18483       this.rules = block.normal;
18484     
18485       if (this.options.gfm) {
18486         if (this.options.tables) {
18487           this.rules = block.tables;
18488         } else {
18489           this.rules = block.gfm;
18490         }
18491       }
18492     }
18493     
18494     /**
18495      * Expose Block Rules
18496      */
18497     
18498     Lexer.rules = block;
18499     
18500     /**
18501      * Static Lex Method
18502      */
18503     
18504     Lexer.lex = function(src, options) {
18505       var lexer = new Lexer(options);
18506       return lexer.lex(src);
18507     };
18508     
18509     /**
18510      * Preprocessing
18511      */
18512     
18513     Lexer.prototype.lex = function(src) {
18514       src = src
18515         .replace(/\r\n|\r/g, '\n')
18516         .replace(/\t/g, '    ')
18517         .replace(/\u00a0/g, ' ')
18518         .replace(/\u2424/g, '\n');
18519     
18520       return this.token(src, true);
18521     };
18522     
18523     /**
18524      * Lexing
18525      */
18526     
18527     Lexer.prototype.token = function(src, top, bq) {
18528       var src = src.replace(/^ +$/gm, '')
18529         , next
18530         , loose
18531         , cap
18532         , bull
18533         , b
18534         , item
18535         , space
18536         , i
18537         , l;
18538     
18539       while (src) {
18540         // newline
18541         if (cap = this.rules.newline.exec(src)) {
18542           src = src.substring(cap[0].length);
18543           if (cap[0].length > 1) {
18544             this.tokens.push({
18545               type: 'space'
18546             });
18547           }
18548         }
18549     
18550         // code
18551         if (cap = this.rules.code.exec(src)) {
18552           src = src.substring(cap[0].length);
18553           cap = cap[0].replace(/^ {4}/gm, '');
18554           this.tokens.push({
18555             type: 'code',
18556             text: !this.options.pedantic
18557               ? cap.replace(/\n+$/, '')
18558               : cap
18559           });
18560           continue;
18561         }
18562     
18563         // fences (gfm)
18564         if (cap = this.rules.fences.exec(src)) {
18565           src = src.substring(cap[0].length);
18566           this.tokens.push({
18567             type: 'code',
18568             lang: cap[2],
18569             text: cap[3] || ''
18570           });
18571           continue;
18572         }
18573     
18574         // heading
18575         if (cap = this.rules.heading.exec(src)) {
18576           src = src.substring(cap[0].length);
18577           this.tokens.push({
18578             type: 'heading',
18579             depth: cap[1].length,
18580             text: cap[2]
18581           });
18582           continue;
18583         }
18584     
18585         // table no leading pipe (gfm)
18586         if (top && (cap = this.rules.nptable.exec(src))) {
18587           src = src.substring(cap[0].length);
18588     
18589           item = {
18590             type: 'table',
18591             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18592             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18593             cells: cap[3].replace(/\n$/, '').split('\n')
18594           };
18595     
18596           for (i = 0; i < item.align.length; i++) {
18597             if (/^ *-+: *$/.test(item.align[i])) {
18598               item.align[i] = 'right';
18599             } else if (/^ *:-+: *$/.test(item.align[i])) {
18600               item.align[i] = 'center';
18601             } else if (/^ *:-+ *$/.test(item.align[i])) {
18602               item.align[i] = 'left';
18603             } else {
18604               item.align[i] = null;
18605             }
18606           }
18607     
18608           for (i = 0; i < item.cells.length; i++) {
18609             item.cells[i] = item.cells[i].split(/ *\| */);
18610           }
18611     
18612           this.tokens.push(item);
18613     
18614           continue;
18615         }
18616     
18617         // lheading
18618         if (cap = this.rules.lheading.exec(src)) {
18619           src = src.substring(cap[0].length);
18620           this.tokens.push({
18621             type: 'heading',
18622             depth: cap[2] === '=' ? 1 : 2,
18623             text: cap[1]
18624           });
18625           continue;
18626         }
18627     
18628         // hr
18629         if (cap = this.rules.hr.exec(src)) {
18630           src = src.substring(cap[0].length);
18631           this.tokens.push({
18632             type: 'hr'
18633           });
18634           continue;
18635         }
18636     
18637         // blockquote
18638         if (cap = this.rules.blockquote.exec(src)) {
18639           src = src.substring(cap[0].length);
18640     
18641           this.tokens.push({
18642             type: 'blockquote_start'
18643           });
18644     
18645           cap = cap[0].replace(/^ *> ?/gm, '');
18646     
18647           // Pass `top` to keep the current
18648           // "toplevel" state. This is exactly
18649           // how markdown.pl works.
18650           this.token(cap, top, true);
18651     
18652           this.tokens.push({
18653             type: 'blockquote_end'
18654           });
18655     
18656           continue;
18657         }
18658     
18659         // list
18660         if (cap = this.rules.list.exec(src)) {
18661           src = src.substring(cap[0].length);
18662           bull = cap[2];
18663     
18664           this.tokens.push({
18665             type: 'list_start',
18666             ordered: bull.length > 1
18667           });
18668     
18669           // Get each top-level item.
18670           cap = cap[0].match(this.rules.item);
18671     
18672           next = false;
18673           l = cap.length;
18674           i = 0;
18675     
18676           for (; i < l; i++) {
18677             item = cap[i];
18678     
18679             // Remove the list item's bullet
18680             // so it is seen as the next token.
18681             space = item.length;
18682             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18683     
18684             // Outdent whatever the
18685             // list item contains. Hacky.
18686             if (~item.indexOf('\n ')) {
18687               space -= item.length;
18688               item = !this.options.pedantic
18689                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18690                 : item.replace(/^ {1,4}/gm, '');
18691             }
18692     
18693             // Determine whether the next list item belongs here.
18694             // Backpedal if it does not belong in this list.
18695             if (this.options.smartLists && i !== l - 1) {
18696               b = block.bullet.exec(cap[i + 1])[0];
18697               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18698                 src = cap.slice(i + 1).join('\n') + src;
18699                 i = l - 1;
18700               }
18701             }
18702     
18703             // Determine whether item is loose or not.
18704             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18705             // for discount behavior.
18706             loose = next || /\n\n(?!\s*$)/.test(item);
18707             if (i !== l - 1) {
18708               next = item.charAt(item.length - 1) === '\n';
18709               if (!loose) { loose = next; }
18710             }
18711     
18712             this.tokens.push({
18713               type: loose
18714                 ? 'loose_item_start'
18715                 : 'list_item_start'
18716             });
18717     
18718             // Recurse.
18719             this.token(item, false, bq);
18720     
18721             this.tokens.push({
18722               type: 'list_item_end'
18723             });
18724           }
18725     
18726           this.tokens.push({
18727             type: 'list_end'
18728           });
18729     
18730           continue;
18731         }
18732     
18733         // html
18734         if (cap = this.rules.html.exec(src)) {
18735           src = src.substring(cap[0].length);
18736           this.tokens.push({
18737             type: this.options.sanitize
18738               ? 'paragraph'
18739               : 'html',
18740             pre: !this.options.sanitizer
18741               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
18742             text: cap[0]
18743           });
18744           continue;
18745         }
18746     
18747         // def
18748         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
18749           src = src.substring(cap[0].length);
18750           this.tokens.links[cap[1].toLowerCase()] = {
18751             href: cap[2],
18752             title: cap[3]
18753           };
18754           continue;
18755         }
18756     
18757         // table (gfm)
18758         if (top && (cap = this.rules.table.exec(src))) {
18759           src = src.substring(cap[0].length);
18760     
18761           item = {
18762             type: 'table',
18763             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18764             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18765             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
18766           };
18767     
18768           for (i = 0; i < item.align.length; i++) {
18769             if (/^ *-+: *$/.test(item.align[i])) {
18770               item.align[i] = 'right';
18771             } else if (/^ *:-+: *$/.test(item.align[i])) {
18772               item.align[i] = 'center';
18773             } else if (/^ *:-+ *$/.test(item.align[i])) {
18774               item.align[i] = 'left';
18775             } else {
18776               item.align[i] = null;
18777             }
18778           }
18779     
18780           for (i = 0; i < item.cells.length; i++) {
18781             item.cells[i] = item.cells[i]
18782               .replace(/^ *\| *| *\| *$/g, '')
18783               .split(/ *\| */);
18784           }
18785     
18786           this.tokens.push(item);
18787     
18788           continue;
18789         }
18790     
18791         // top-level paragraph
18792         if (top && (cap = this.rules.paragraph.exec(src))) {
18793           src = src.substring(cap[0].length);
18794           this.tokens.push({
18795             type: 'paragraph',
18796             text: cap[1].charAt(cap[1].length - 1) === '\n'
18797               ? cap[1].slice(0, -1)
18798               : cap[1]
18799           });
18800           continue;
18801         }
18802     
18803         // text
18804         if (cap = this.rules.text.exec(src)) {
18805           // Top-level should never reach here.
18806           src = src.substring(cap[0].length);
18807           this.tokens.push({
18808             type: 'text',
18809             text: cap[0]
18810           });
18811           continue;
18812         }
18813     
18814         if (src) {
18815           throw new
18816             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18817         }
18818       }
18819     
18820       return this.tokens;
18821     };
18822     
18823     /**
18824      * Inline-Level Grammar
18825      */
18826     
18827     var inline = {
18828       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18829       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18830       url: noop,
18831       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18832       link: /^!?\[(inside)\]\(href\)/,
18833       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18834       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18835       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18836       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18837       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18838       br: /^ {2,}\n(?!\s*$)/,
18839       del: noop,
18840       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18841     };
18842     
18843     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
18844     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18845     
18846     inline.link = replace(inline.link)
18847       ('inside', inline._inside)
18848       ('href', inline._href)
18849       ();
18850     
18851     inline.reflink = replace(inline.reflink)
18852       ('inside', inline._inside)
18853       ();
18854     
18855     /**
18856      * Normal Inline Grammar
18857      */
18858     
18859     inline.normal = merge({}, inline);
18860     
18861     /**
18862      * Pedantic Inline Grammar
18863      */
18864     
18865     inline.pedantic = merge({}, inline.normal, {
18866       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18867       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18868     });
18869     
18870     /**
18871      * GFM Inline Grammar
18872      */
18873     
18874     inline.gfm = merge({}, inline.normal, {
18875       escape: replace(inline.escape)('])', '~|])')(),
18876       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18877       del: /^~~(?=\S)([\s\S]*?\S)~~/,
18878       text: replace(inline.text)
18879         (']|', '~]|')
18880         ('|', '|https?://|')
18881         ()
18882     });
18883     
18884     /**
18885      * GFM + Line Breaks Inline Grammar
18886      */
18887     
18888     inline.breaks = merge({}, inline.gfm, {
18889       br: replace(inline.br)('{2,}', '*')(),
18890       text: replace(inline.gfm.text)('{2,}', '*')()
18891     });
18892     
18893     /**
18894      * Inline Lexer & Compiler
18895      */
18896     
18897     var InlineLexer  = function (links, options) {
18898       this.options = options || marked.defaults;
18899       this.links = links;
18900       this.rules = inline.normal;
18901       this.renderer = this.options.renderer || new Renderer;
18902       this.renderer.options = this.options;
18903     
18904       if (!this.links) {
18905         throw new
18906           Error('Tokens array requires a `links` property.');
18907       }
18908     
18909       if (this.options.gfm) {
18910         if (this.options.breaks) {
18911           this.rules = inline.breaks;
18912         } else {
18913           this.rules = inline.gfm;
18914         }
18915       } else if (this.options.pedantic) {
18916         this.rules = inline.pedantic;
18917       }
18918     }
18919     
18920     /**
18921      * Expose Inline Rules
18922      */
18923     
18924     InlineLexer.rules = inline;
18925     
18926     /**
18927      * Static Lexing/Compiling Method
18928      */
18929     
18930     InlineLexer.output = function(src, links, options) {
18931       var inline = new InlineLexer(links, options);
18932       return inline.output(src);
18933     };
18934     
18935     /**
18936      * Lexing/Compiling
18937      */
18938     
18939     InlineLexer.prototype.output = function(src) {
18940       var out = ''
18941         , link
18942         , text
18943         , href
18944         , cap;
18945     
18946       while (src) {
18947         // escape
18948         if (cap = this.rules.escape.exec(src)) {
18949           src = src.substring(cap[0].length);
18950           out += cap[1];
18951           continue;
18952         }
18953     
18954         // autolink
18955         if (cap = this.rules.autolink.exec(src)) {
18956           src = src.substring(cap[0].length);
18957           if (cap[2] === '@') {
18958             text = cap[1].charAt(6) === ':'
18959               ? this.mangle(cap[1].substring(7))
18960               : this.mangle(cap[1]);
18961             href = this.mangle('mailto:') + text;
18962           } else {
18963             text = escape(cap[1]);
18964             href = text;
18965           }
18966           out += this.renderer.link(href, null, text);
18967           continue;
18968         }
18969     
18970         // url (gfm)
18971         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18972           src = src.substring(cap[0].length);
18973           text = escape(cap[1]);
18974           href = text;
18975           out += this.renderer.link(href, null, text);
18976           continue;
18977         }
18978     
18979         // tag
18980         if (cap = this.rules.tag.exec(src)) {
18981           if (!this.inLink && /^<a /i.test(cap[0])) {
18982             this.inLink = true;
18983           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18984             this.inLink = false;
18985           }
18986           src = src.substring(cap[0].length);
18987           out += this.options.sanitize
18988             ? this.options.sanitizer
18989               ? this.options.sanitizer(cap[0])
18990               : escape(cap[0])
18991             : cap[0];
18992           continue;
18993         }
18994     
18995         // link
18996         if (cap = this.rules.link.exec(src)) {
18997           src = src.substring(cap[0].length);
18998           this.inLink = true;
18999           out += this.outputLink(cap, {
19000             href: cap[2],
19001             title: cap[3]
19002           });
19003           this.inLink = false;
19004           continue;
19005         }
19006     
19007         // reflink, nolink
19008         if ((cap = this.rules.reflink.exec(src))
19009             || (cap = this.rules.nolink.exec(src))) {
19010           src = src.substring(cap[0].length);
19011           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19012           link = this.links[link.toLowerCase()];
19013           if (!link || !link.href) {
19014             out += cap[0].charAt(0);
19015             src = cap[0].substring(1) + src;
19016             continue;
19017           }
19018           this.inLink = true;
19019           out += this.outputLink(cap, link);
19020           this.inLink = false;
19021           continue;
19022         }
19023     
19024         // strong
19025         if (cap = this.rules.strong.exec(src)) {
19026           src = src.substring(cap[0].length);
19027           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19028           continue;
19029         }
19030     
19031         // em
19032         if (cap = this.rules.em.exec(src)) {
19033           src = src.substring(cap[0].length);
19034           out += this.renderer.em(this.output(cap[2] || cap[1]));
19035           continue;
19036         }
19037     
19038         // code
19039         if (cap = this.rules.code.exec(src)) {
19040           src = src.substring(cap[0].length);
19041           out += this.renderer.codespan(escape(cap[2], true));
19042           continue;
19043         }
19044     
19045         // br
19046         if (cap = this.rules.br.exec(src)) {
19047           src = src.substring(cap[0].length);
19048           out += this.renderer.br();
19049           continue;
19050         }
19051     
19052         // del (gfm)
19053         if (cap = this.rules.del.exec(src)) {
19054           src = src.substring(cap[0].length);
19055           out += this.renderer.del(this.output(cap[1]));
19056           continue;
19057         }
19058     
19059         // text
19060         if (cap = this.rules.text.exec(src)) {
19061           src = src.substring(cap[0].length);
19062           out += this.renderer.text(escape(this.smartypants(cap[0])));
19063           continue;
19064         }
19065     
19066         if (src) {
19067           throw new
19068             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19069         }
19070       }
19071     
19072       return out;
19073     };
19074     
19075     /**
19076      * Compile Link
19077      */
19078     
19079     InlineLexer.prototype.outputLink = function(cap, link) {
19080       var href = escape(link.href)
19081         , title = link.title ? escape(link.title) : null;
19082     
19083       return cap[0].charAt(0) !== '!'
19084         ? this.renderer.link(href, title, this.output(cap[1]))
19085         : this.renderer.image(href, title, escape(cap[1]));
19086     };
19087     
19088     /**
19089      * Smartypants Transformations
19090      */
19091     
19092     InlineLexer.prototype.smartypants = function(text) {
19093       if (!this.options.smartypants)  { return text; }
19094       return text
19095         // em-dashes
19096         .replace(/---/g, '\u2014')
19097         // en-dashes
19098         .replace(/--/g, '\u2013')
19099         // opening singles
19100         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19101         // closing singles & apostrophes
19102         .replace(/'/g, '\u2019')
19103         // opening doubles
19104         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19105         // closing doubles
19106         .replace(/"/g, '\u201d')
19107         // ellipses
19108         .replace(/\.{3}/g, '\u2026');
19109     };
19110     
19111     /**
19112      * Mangle Links
19113      */
19114     
19115     InlineLexer.prototype.mangle = function(text) {
19116       if (!this.options.mangle) { return text; }
19117       var out = ''
19118         , l = text.length
19119         , i = 0
19120         , ch;
19121     
19122       for (; i < l; i++) {
19123         ch = text.charCodeAt(i);
19124         if (Math.random() > 0.5) {
19125           ch = 'x' + ch.toString(16);
19126         }
19127         out += '&#' + ch + ';';
19128       }
19129     
19130       return out;
19131     };
19132     
19133     /**
19134      * Renderer
19135      */
19136     
19137      /**
19138          * eval:var:Renderer
19139     */
19140     
19141     var Renderer   = function (options) {
19142       this.options = options || {};
19143     }
19144     
19145     Renderer.prototype.code = function(code, lang, escaped) {
19146       if (this.options.highlight) {
19147         var out = this.options.highlight(code, lang);
19148         if (out != null && out !== code) {
19149           escaped = true;
19150           code = out;
19151         }
19152       } else {
19153             // hack!!! - it's already escapeD?
19154             escaped = true;
19155       }
19156     
19157       if (!lang) {
19158         return '<pre><code>'
19159           + (escaped ? code : escape(code, true))
19160           + '\n</code></pre>';
19161       }
19162     
19163       return '<pre><code class="'
19164         + this.options.langPrefix
19165         + escape(lang, true)
19166         + '">'
19167         + (escaped ? code : escape(code, true))
19168         + '\n</code></pre>\n';
19169     };
19170     
19171     Renderer.prototype.blockquote = function(quote) {
19172       return '<blockquote>\n' + quote + '</blockquote>\n';
19173     };
19174     
19175     Renderer.prototype.html = function(html) {
19176       return html;
19177     };
19178     
19179     Renderer.prototype.heading = function(text, level, raw) {
19180       return '<h'
19181         + level
19182         + ' id="'
19183         + this.options.headerPrefix
19184         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19185         + '">'
19186         + text
19187         + '</h'
19188         + level
19189         + '>\n';
19190     };
19191     
19192     Renderer.prototype.hr = function() {
19193       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19194     };
19195     
19196     Renderer.prototype.list = function(body, ordered) {
19197       var type = ordered ? 'ol' : 'ul';
19198       return '<' + type + '>\n' + body + '</' + type + '>\n';
19199     };
19200     
19201     Renderer.prototype.listitem = function(text) {
19202       return '<li>' + text + '</li>\n';
19203     };
19204     
19205     Renderer.prototype.paragraph = function(text) {
19206       return '<p>' + text + '</p>\n';
19207     };
19208     
19209     Renderer.prototype.table = function(header, body) {
19210       return '<table class="table table-striped">\n'
19211         + '<thead>\n'
19212         + header
19213         + '</thead>\n'
19214         + '<tbody>\n'
19215         + body
19216         + '</tbody>\n'
19217         + '</table>\n';
19218     };
19219     
19220     Renderer.prototype.tablerow = function(content) {
19221       return '<tr>\n' + content + '</tr>\n';
19222     };
19223     
19224     Renderer.prototype.tablecell = function(content, flags) {
19225       var type = flags.header ? 'th' : 'td';
19226       var tag = flags.align
19227         ? '<' + type + ' style="text-align:' + flags.align + '">'
19228         : '<' + type + '>';
19229       return tag + content + '</' + type + '>\n';
19230     };
19231     
19232     // span level renderer
19233     Renderer.prototype.strong = function(text) {
19234       return '<strong>' + text + '</strong>';
19235     };
19236     
19237     Renderer.prototype.em = function(text) {
19238       return '<em>' + text + '</em>';
19239     };
19240     
19241     Renderer.prototype.codespan = function(text) {
19242       return '<code>' + text + '</code>';
19243     };
19244     
19245     Renderer.prototype.br = function() {
19246       return this.options.xhtml ? '<br/>' : '<br>';
19247     };
19248     
19249     Renderer.prototype.del = function(text) {
19250       return '<del>' + text + '</del>';
19251     };
19252     
19253     Renderer.prototype.link = function(href, title, text) {
19254       if (this.options.sanitize) {
19255         try {
19256           var prot = decodeURIComponent(unescape(href))
19257             .replace(/[^\w:]/g, '')
19258             .toLowerCase();
19259         } catch (e) {
19260           return '';
19261         }
19262         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19263           return '';
19264         }
19265       }
19266       var out = '<a href="' + href + '"';
19267       if (title) {
19268         out += ' title="' + title + '"';
19269       }
19270       out += '>' + text + '</a>';
19271       return out;
19272     };
19273     
19274     Renderer.prototype.image = function(href, title, text) {
19275       var out = '<img src="' + href + '" alt="' + text + '"';
19276       if (title) {
19277         out += ' title="' + title + '"';
19278       }
19279       out += this.options.xhtml ? '/>' : '>';
19280       return out;
19281     };
19282     
19283     Renderer.prototype.text = function(text) {
19284       return text;
19285     };
19286     
19287     /**
19288      * Parsing & Compiling
19289      */
19290          /**
19291          * eval:var:Parser
19292     */
19293     
19294     var Parser= function (options) {
19295       this.tokens = [];
19296       this.token = null;
19297       this.options = options || marked.defaults;
19298       this.options.renderer = this.options.renderer || new Renderer;
19299       this.renderer = this.options.renderer;
19300       this.renderer.options = this.options;
19301     }
19302     
19303     /**
19304      * Static Parse Method
19305      */
19306     
19307     Parser.parse = function(src, options, renderer) {
19308       var parser = new Parser(options, renderer);
19309       return parser.parse(src);
19310     };
19311     
19312     /**
19313      * Parse Loop
19314      */
19315     
19316     Parser.prototype.parse = function(src) {
19317       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19318       this.tokens = src.reverse();
19319     
19320       var out = '';
19321       while (this.next()) {
19322         out += this.tok();
19323       }
19324     
19325       return out;
19326     };
19327     
19328     /**
19329      * Next Token
19330      */
19331     
19332     Parser.prototype.next = function() {
19333       return this.token = this.tokens.pop();
19334     };
19335     
19336     /**
19337      * Preview Next Token
19338      */
19339     
19340     Parser.prototype.peek = function() {
19341       return this.tokens[this.tokens.length - 1] || 0;
19342     };
19343     
19344     /**
19345      * Parse Text Tokens
19346      */
19347     
19348     Parser.prototype.parseText = function() {
19349       var body = this.token.text;
19350     
19351       while (this.peek().type === 'text') {
19352         body += '\n' + this.next().text;
19353       }
19354     
19355       return this.inline.output(body);
19356     };
19357     
19358     /**
19359      * Parse Current Token
19360      */
19361     
19362     Parser.prototype.tok = function() {
19363       switch (this.token.type) {
19364         case 'space': {
19365           return '';
19366         }
19367         case 'hr': {
19368           return this.renderer.hr();
19369         }
19370         case 'heading': {
19371           return this.renderer.heading(
19372             this.inline.output(this.token.text),
19373             this.token.depth,
19374             this.token.text);
19375         }
19376         case 'code': {
19377           return this.renderer.code(this.token.text,
19378             this.token.lang,
19379             this.token.escaped);
19380         }
19381         case 'table': {
19382           var header = ''
19383             , body = ''
19384             , i
19385             , row
19386             , cell
19387             , flags
19388             , j;
19389     
19390           // header
19391           cell = '';
19392           for (i = 0; i < this.token.header.length; i++) {
19393             flags = { header: true, align: this.token.align[i] };
19394             cell += this.renderer.tablecell(
19395               this.inline.output(this.token.header[i]),
19396               { header: true, align: this.token.align[i] }
19397             );
19398           }
19399           header += this.renderer.tablerow(cell);
19400     
19401           for (i = 0; i < this.token.cells.length; i++) {
19402             row = this.token.cells[i];
19403     
19404             cell = '';
19405             for (j = 0; j < row.length; j++) {
19406               cell += this.renderer.tablecell(
19407                 this.inline.output(row[j]),
19408                 { header: false, align: this.token.align[j] }
19409               );
19410             }
19411     
19412             body += this.renderer.tablerow(cell);
19413           }
19414           return this.renderer.table(header, body);
19415         }
19416         case 'blockquote_start': {
19417           var body = '';
19418     
19419           while (this.next().type !== 'blockquote_end') {
19420             body += this.tok();
19421           }
19422     
19423           return this.renderer.blockquote(body);
19424         }
19425         case 'list_start': {
19426           var body = ''
19427             , ordered = this.token.ordered;
19428     
19429           while (this.next().type !== 'list_end') {
19430             body += this.tok();
19431           }
19432     
19433           return this.renderer.list(body, ordered);
19434         }
19435         case 'list_item_start': {
19436           var body = '';
19437     
19438           while (this.next().type !== 'list_item_end') {
19439             body += this.token.type === 'text'
19440               ? this.parseText()
19441               : this.tok();
19442           }
19443     
19444           return this.renderer.listitem(body);
19445         }
19446         case 'loose_item_start': {
19447           var body = '';
19448     
19449           while (this.next().type !== 'list_item_end') {
19450             body += this.tok();
19451           }
19452     
19453           return this.renderer.listitem(body);
19454         }
19455         case 'html': {
19456           var html = !this.token.pre && !this.options.pedantic
19457             ? this.inline.output(this.token.text)
19458             : this.token.text;
19459           return this.renderer.html(html);
19460         }
19461         case 'paragraph': {
19462           return this.renderer.paragraph(this.inline.output(this.token.text));
19463         }
19464         case 'text': {
19465           return this.renderer.paragraph(this.parseText());
19466         }
19467       }
19468     };
19469   
19470     
19471     /**
19472      * Marked
19473      */
19474          /**
19475          * eval:var:marked
19476     */
19477     var marked = function (src, opt, callback) {
19478       if (callback || typeof opt === 'function') {
19479         if (!callback) {
19480           callback = opt;
19481           opt = null;
19482         }
19483     
19484         opt = merge({}, marked.defaults, opt || {});
19485     
19486         var highlight = opt.highlight
19487           , tokens
19488           , pending
19489           , i = 0;
19490     
19491         try {
19492           tokens = Lexer.lex(src, opt)
19493         } catch (e) {
19494           return callback(e);
19495         }
19496     
19497         pending = tokens.length;
19498          /**
19499          * eval:var:done
19500     */
19501         var done = function(err) {
19502           if (err) {
19503             opt.highlight = highlight;
19504             return callback(err);
19505           }
19506     
19507           var out;
19508     
19509           try {
19510             out = Parser.parse(tokens, opt);
19511           } catch (e) {
19512             err = e;
19513           }
19514     
19515           opt.highlight = highlight;
19516     
19517           return err
19518             ? callback(err)
19519             : callback(null, out);
19520         };
19521     
19522         if (!highlight || highlight.length < 3) {
19523           return done();
19524         }
19525     
19526         delete opt.highlight;
19527     
19528         if (!pending) { return done(); }
19529     
19530         for (; i < tokens.length; i++) {
19531           (function(token) {
19532             if (token.type !== 'code') {
19533               return --pending || done();
19534             }
19535             return highlight(token.text, token.lang, function(err, code) {
19536               if (err) { return done(err); }
19537               if (code == null || code === token.text) {
19538                 return --pending || done();
19539               }
19540               token.text = code;
19541               token.escaped = true;
19542               --pending || done();
19543             });
19544           })(tokens[i]);
19545         }
19546     
19547         return;
19548       }
19549       try {
19550         if (opt) { opt = merge({}, marked.defaults, opt); }
19551         return Parser.parse(Lexer.lex(src, opt), opt);
19552       } catch (e) {
19553         e.message += '\nPlease report this to https://github.com/chjj/marked.';
19554         if ((opt || marked.defaults).silent) {
19555           return '<p>An error occured:</p><pre>'
19556             + escape(e.message + '', true)
19557             + '</pre>';
19558         }
19559         throw e;
19560       }
19561     }
19562     
19563     /**
19564      * Options
19565      */
19566     
19567     marked.options =
19568     marked.setOptions = function(opt) {
19569       merge(marked.defaults, opt);
19570       return marked;
19571     };
19572     
19573     marked.defaults = {
19574       gfm: true,
19575       tables: true,
19576       breaks: false,
19577       pedantic: false,
19578       sanitize: false,
19579       sanitizer: null,
19580       mangle: true,
19581       smartLists: false,
19582       silent: false,
19583       highlight: null,
19584       langPrefix: 'lang-',
19585       smartypants: false,
19586       headerPrefix: '',
19587       renderer: new Renderer,
19588       xhtml: false
19589     };
19590     
19591     /**
19592      * Expose
19593      */
19594     
19595     marked.Parser = Parser;
19596     marked.parser = Parser.parse;
19597     
19598     marked.Renderer = Renderer;
19599     
19600     marked.Lexer = Lexer;
19601     marked.lexer = Lexer.lex;
19602     
19603     marked.InlineLexer = InlineLexer;
19604     marked.inlineLexer = InlineLexer.output;
19605     
19606     marked.parse = marked;
19607     
19608     Roo.Markdown.marked = marked;
19609
19610 })();/*
19611  * Based on:
19612  * Ext JS Library 1.1.1
19613  * Copyright(c) 2006-2007, Ext JS, LLC.
19614  *
19615  * Originally Released Under LGPL - original licence link has changed is not relivant.
19616  *
19617  * Fork - LGPL
19618  * <script type="text/javascript">
19619  */
19620
19621
19622
19623 /*
19624  * These classes are derivatives of the similarly named classes in the YUI Library.
19625  * The original license:
19626  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19627  * Code licensed under the BSD License:
19628  * http://developer.yahoo.net/yui/license.txt
19629  */
19630
19631 (function() {
19632
19633 var Event=Roo.EventManager;
19634 var Dom=Roo.lib.Dom;
19635
19636 /**
19637  * @class Roo.dd.DragDrop
19638  * @extends Roo.util.Observable
19639  * Defines the interface and base operation of items that that can be
19640  * dragged or can be drop targets.  It was designed to be extended, overriding
19641  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19642  * Up to three html elements can be associated with a DragDrop instance:
19643  * <ul>
19644  * <li>linked element: the element that is passed into the constructor.
19645  * This is the element which defines the boundaries for interaction with
19646  * other DragDrop objects.</li>
19647  * <li>handle element(s): The drag operation only occurs if the element that
19648  * was clicked matches a handle element.  By default this is the linked
19649  * element, but there are times that you will want only a portion of the
19650  * linked element to initiate the drag operation, and the setHandleElId()
19651  * method provides a way to define this.</li>
19652  * <li>drag element: this represents the element that would be moved along
19653  * with the cursor during a drag operation.  By default, this is the linked
19654  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
19655  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19656  * </li>
19657  * </ul>
19658  * This class should not be instantiated until the onload event to ensure that
19659  * the associated elements are available.
19660  * The following would define a DragDrop obj that would interact with any
19661  * other DragDrop obj in the "group1" group:
19662  * <pre>
19663  *  dd = new Roo.dd.DragDrop("div1", "group1");
19664  * </pre>
19665  * Since none of the event handlers have been implemented, nothing would
19666  * actually happen if you were to run the code above.  Normally you would
19667  * override this class or one of the default implementations, but you can
19668  * also override the methods you want on an instance of the class...
19669  * <pre>
19670  *  dd.onDragDrop = function(e, id) {
19671  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
19672  *  }
19673  * </pre>
19674  * @constructor
19675  * @param {String} id of the element that is linked to this instance
19676  * @param {String} sGroup the group of related DragDrop objects
19677  * @param {object} config an object containing configurable attributes
19678  *                Valid properties for DragDrop:
19679  *                    padding, isTarget, maintainOffset, primaryButtonOnly
19680  */
19681 Roo.dd.DragDrop = function(id, sGroup, config) {
19682     if (id) {
19683         this.init(id, sGroup, config);
19684     }
19685     
19686 };
19687
19688 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
19689
19690     /**
19691      * The id of the element associated with this object.  This is what we
19692      * refer to as the "linked element" because the size and position of
19693      * this element is used to determine when the drag and drop objects have
19694      * interacted.
19695      * @property id
19696      * @type String
19697      */
19698     id: null,
19699
19700     /**
19701      * Configuration attributes passed into the constructor
19702      * @property config
19703      * @type object
19704      */
19705     config: null,
19706
19707     /**
19708      * The id of the element that will be dragged.  By default this is same
19709      * as the linked element , but could be changed to another element. Ex:
19710      * Roo.dd.DDProxy
19711      * @property dragElId
19712      * @type String
19713      * @private
19714      */
19715     dragElId: null,
19716
19717     /**
19718      * the id of the element that initiates the drag operation.  By default
19719      * this is the linked element, but could be changed to be a child of this
19720      * element.  This lets us do things like only starting the drag when the
19721      * header element within the linked html element is clicked.
19722      * @property handleElId
19723      * @type String
19724      * @private
19725      */
19726     handleElId: null,
19727
19728     /**
19729      * An associative array of HTML tags that will be ignored if clicked.
19730      * @property invalidHandleTypes
19731      * @type {string: string}
19732      */
19733     invalidHandleTypes: null,
19734
19735     /**
19736      * An associative array of ids for elements that will be ignored if clicked
19737      * @property invalidHandleIds
19738      * @type {string: string}
19739      */
19740     invalidHandleIds: null,
19741
19742     /**
19743      * An indexted array of css class names for elements that will be ignored
19744      * if clicked.
19745      * @property invalidHandleClasses
19746      * @type string[]
19747      */
19748     invalidHandleClasses: null,
19749
19750     /**
19751      * The linked element's absolute X position at the time the drag was
19752      * started
19753      * @property startPageX
19754      * @type int
19755      * @private
19756      */
19757     startPageX: 0,
19758
19759     /**
19760      * The linked element's absolute X position at the time the drag was
19761      * started
19762      * @property startPageY
19763      * @type int
19764      * @private
19765      */
19766     startPageY: 0,
19767
19768     /**
19769      * The group defines a logical collection of DragDrop objects that are
19770      * related.  Instances only get events when interacting with other
19771      * DragDrop object in the same group.  This lets us define multiple
19772      * groups using a single DragDrop subclass if we want.
19773      * @property groups
19774      * @type {string: string}
19775      */
19776     groups: null,
19777
19778     /**
19779      * Individual drag/drop instances can be locked.  This will prevent
19780      * onmousedown start drag.
19781      * @property locked
19782      * @type boolean
19783      * @private
19784      */
19785     locked: false,
19786
19787     /**
19788      * Lock this instance
19789      * @method lock
19790      */
19791     lock: function() { this.locked = true; },
19792
19793     /**
19794      * Unlock this instace
19795      * @method unlock
19796      */
19797     unlock: function() { this.locked = false; },
19798
19799     /**
19800      * By default, all insances can be a drop target.  This can be disabled by
19801      * setting isTarget to false.
19802      * @method isTarget
19803      * @type boolean
19804      */
19805     isTarget: true,
19806
19807     /**
19808      * The padding configured for this drag and drop object for calculating
19809      * the drop zone intersection with this object.
19810      * @method padding
19811      * @type int[]
19812      */
19813     padding: null,
19814
19815     /**
19816      * Cached reference to the linked element
19817      * @property _domRef
19818      * @private
19819      */
19820     _domRef: null,
19821
19822     /**
19823      * Internal typeof flag
19824      * @property __ygDragDrop
19825      * @private
19826      */
19827     __ygDragDrop: true,
19828
19829     /**
19830      * Set to true when horizontal contraints are applied
19831      * @property constrainX
19832      * @type boolean
19833      * @private
19834      */
19835     constrainX: false,
19836
19837     /**
19838      * Set to true when vertical contraints are applied
19839      * @property constrainY
19840      * @type boolean
19841      * @private
19842      */
19843     constrainY: false,
19844
19845     /**
19846      * The left constraint
19847      * @property minX
19848      * @type int
19849      * @private
19850      */
19851     minX: 0,
19852
19853     /**
19854      * The right constraint
19855      * @property maxX
19856      * @type int
19857      * @private
19858      */
19859     maxX: 0,
19860
19861     /**
19862      * The up constraint
19863      * @property minY
19864      * @type int
19865      * @type int
19866      * @private
19867      */
19868     minY: 0,
19869
19870     /**
19871      * The down constraint
19872      * @property maxY
19873      * @type int
19874      * @private
19875      */
19876     maxY: 0,
19877
19878     /**
19879      * Maintain offsets when we resetconstraints.  Set to true when you want
19880      * the position of the element relative to its parent to stay the same
19881      * when the page changes
19882      *
19883      * @property maintainOffset
19884      * @type boolean
19885      */
19886     maintainOffset: false,
19887
19888     /**
19889      * Array of pixel locations the element will snap to if we specified a
19890      * horizontal graduation/interval.  This array is generated automatically
19891      * when you define a tick interval.
19892      * @property xTicks
19893      * @type int[]
19894      */
19895     xTicks: null,
19896
19897     /**
19898      * Array of pixel locations the element will snap to if we specified a
19899      * vertical graduation/interval.  This array is generated automatically
19900      * when you define a tick interval.
19901      * @property yTicks
19902      * @type int[]
19903      */
19904     yTicks: null,
19905
19906     /**
19907      * By default the drag and drop instance will only respond to the primary
19908      * button click (left button for a right-handed mouse).  Set to true to
19909      * allow drag and drop to start with any mouse click that is propogated
19910      * by the browser
19911      * @property primaryButtonOnly
19912      * @type boolean
19913      */
19914     primaryButtonOnly: true,
19915
19916     /**
19917      * The availabe property is false until the linked dom element is accessible.
19918      * @property available
19919      * @type boolean
19920      */
19921     available: false,
19922
19923     /**
19924      * By default, drags can only be initiated if the mousedown occurs in the
19925      * region the linked element is.  This is done in part to work around a
19926      * bug in some browsers that mis-report the mousedown if the previous
19927      * mouseup happened outside of the window.  This property is set to true
19928      * if outer handles are defined.
19929      *
19930      * @property hasOuterHandles
19931      * @type boolean
19932      * @default false
19933      */
19934     hasOuterHandles: false,
19935
19936     /**
19937      * Code that executes immediately before the startDrag event
19938      * @method b4StartDrag
19939      * @private
19940      */
19941     b4StartDrag: function(x, y) { },
19942
19943     /**
19944      * Abstract method called after a drag/drop object is clicked
19945      * and the drag or mousedown time thresholds have beeen met.
19946      * @method startDrag
19947      * @param {int} X click location
19948      * @param {int} Y click location
19949      */
19950     startDrag: function(x, y) { /* override this */ },
19951
19952     /**
19953      * Code that executes immediately before the onDrag event
19954      * @method b4Drag
19955      * @private
19956      */
19957     b4Drag: function(e) { },
19958
19959     /**
19960      * Abstract method called during the onMouseMove event while dragging an
19961      * object.
19962      * @method onDrag
19963      * @param {Event} e the mousemove event
19964      */
19965     onDrag: function(e) { /* override this */ },
19966
19967     /**
19968      * Abstract method called when this element fist begins hovering over
19969      * another DragDrop obj
19970      * @method onDragEnter
19971      * @param {Event} e the mousemove event
19972      * @param {String|DragDrop[]} id In POINT mode, the element
19973      * id this is hovering over.  In INTERSECT mode, an array of one or more
19974      * dragdrop items being hovered over.
19975      */
19976     onDragEnter: function(e, id) { /* override this */ },
19977
19978     /**
19979      * Code that executes immediately before the onDragOver event
19980      * @method b4DragOver
19981      * @private
19982      */
19983     b4DragOver: function(e) { },
19984
19985     /**
19986      * Abstract method called when this element is hovering over another
19987      * DragDrop obj
19988      * @method onDragOver
19989      * @param {Event} e the mousemove event
19990      * @param {String|DragDrop[]} id In POINT mode, the element
19991      * id this is hovering over.  In INTERSECT mode, an array of dd items
19992      * being hovered over.
19993      */
19994     onDragOver: function(e, id) { /* override this */ },
19995
19996     /**
19997      * Code that executes immediately before the onDragOut event
19998      * @method b4DragOut
19999      * @private
20000      */
20001     b4DragOut: function(e) { },
20002
20003     /**
20004      * Abstract method called when we are no longer hovering over an element
20005      * @method onDragOut
20006      * @param {Event} e the mousemove event
20007      * @param {String|DragDrop[]} id In POINT mode, the element
20008      * id this was hovering over.  In INTERSECT mode, an array of dd items
20009      * that the mouse is no longer over.
20010      */
20011     onDragOut: function(e, id) { /* override this */ },
20012
20013     /**
20014      * Code that executes immediately before the onDragDrop event
20015      * @method b4DragDrop
20016      * @private
20017      */
20018     b4DragDrop: function(e) { },
20019
20020     /**
20021      * Abstract method called when this item is dropped on another DragDrop
20022      * obj
20023      * @method onDragDrop
20024      * @param {Event} e the mouseup event
20025      * @param {String|DragDrop[]} id In POINT mode, the element
20026      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20027      * was dropped on.
20028      */
20029     onDragDrop: function(e, id) { /* override this */ },
20030
20031     /**
20032      * Abstract method called when this item is dropped on an area with no
20033      * drop target
20034      * @method onInvalidDrop
20035      * @param {Event} e the mouseup event
20036      */
20037     onInvalidDrop: function(e) { /* override this */ },
20038
20039     /**
20040      * Code that executes immediately before the endDrag event
20041      * @method b4EndDrag
20042      * @private
20043      */
20044     b4EndDrag: function(e) { },
20045
20046     /**
20047      * Fired when we are done dragging the object
20048      * @method endDrag
20049      * @param {Event} e the mouseup event
20050      */
20051     endDrag: function(e) { /* override this */ },
20052
20053     /**
20054      * Code executed immediately before the onMouseDown event
20055      * @method b4MouseDown
20056      * @param {Event} e the mousedown event
20057      * @private
20058      */
20059     b4MouseDown: function(e) {  },
20060
20061     /**
20062      * Event handler that fires when a drag/drop obj gets a mousedown
20063      * @method onMouseDown
20064      * @param {Event} e the mousedown event
20065      */
20066     onMouseDown: function(e) { /* override this */ },
20067
20068     /**
20069      * Event handler that fires when a drag/drop obj gets a mouseup
20070      * @method onMouseUp
20071      * @param {Event} e the mouseup event
20072      */
20073     onMouseUp: function(e) { /* override this */ },
20074
20075     /**
20076      * Override the onAvailable method to do what is needed after the initial
20077      * position was determined.
20078      * @method onAvailable
20079      */
20080     onAvailable: function () {
20081     },
20082
20083     /*
20084      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20085      * @type Object
20086      */
20087     defaultPadding : {left:0, right:0, top:0, bottom:0},
20088
20089     /*
20090      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20091  *
20092  * Usage:
20093  <pre><code>
20094  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20095                 { dragElId: "existingProxyDiv" });
20096  dd.startDrag = function(){
20097      this.constrainTo("parent-id");
20098  };
20099  </code></pre>
20100  * Or you can initalize it using the {@link Roo.Element} object:
20101  <pre><code>
20102  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20103      startDrag : function(){
20104          this.constrainTo("parent-id");
20105      }
20106  });
20107  </code></pre>
20108      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20109      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20110      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20111      * an object containing the sides to pad. For example: {right:10, bottom:10}
20112      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20113      */
20114     constrainTo : function(constrainTo, pad, inContent){
20115         if(typeof pad == "number"){
20116             pad = {left: pad, right:pad, top:pad, bottom:pad};
20117         }
20118         pad = pad || this.defaultPadding;
20119         var b = Roo.get(this.getEl()).getBox();
20120         var ce = Roo.get(constrainTo);
20121         var s = ce.getScroll();
20122         var c, cd = ce.dom;
20123         if(cd == document.body){
20124             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20125         }else{
20126             xy = ce.getXY();
20127             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20128         }
20129
20130
20131         var topSpace = b.y - c.y;
20132         var leftSpace = b.x - c.x;
20133
20134         this.resetConstraints();
20135         this.setXConstraint(leftSpace - (pad.left||0), // left
20136                 c.width - leftSpace - b.width - (pad.right||0) //right
20137         );
20138         this.setYConstraint(topSpace - (pad.top||0), //top
20139                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20140         );
20141     },
20142
20143     /**
20144      * Returns a reference to the linked element
20145      * @method getEl
20146      * @return {HTMLElement} the html element
20147      */
20148     getEl: function() {
20149         if (!this._domRef) {
20150             this._domRef = Roo.getDom(this.id);
20151         }
20152
20153         return this._domRef;
20154     },
20155
20156     /**
20157      * Returns a reference to the actual element to drag.  By default this is
20158      * the same as the html element, but it can be assigned to another
20159      * element. An example of this can be found in Roo.dd.DDProxy
20160      * @method getDragEl
20161      * @return {HTMLElement} the html element
20162      */
20163     getDragEl: function() {
20164         return Roo.getDom(this.dragElId);
20165     },
20166
20167     /**
20168      * Sets up the DragDrop object.  Must be called in the constructor of any
20169      * Roo.dd.DragDrop subclass
20170      * @method init
20171      * @param id the id of the linked element
20172      * @param {String} sGroup the group of related items
20173      * @param {object} config configuration attributes
20174      */
20175     init: function(id, sGroup, config) {
20176         this.initTarget(id, sGroup, config);
20177         if (!Roo.isTouch) {
20178             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20179         }
20180         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20181         // Event.on(this.id, "selectstart", Event.preventDefault);
20182     },
20183
20184     /**
20185      * Initializes Targeting functionality only... the object does not
20186      * get a mousedown handler.
20187      * @method initTarget
20188      * @param id the id of the linked element
20189      * @param {String} sGroup the group of related items
20190      * @param {object} config configuration attributes
20191      */
20192     initTarget: function(id, sGroup, config) {
20193
20194         // configuration attributes
20195         this.config = config || {};
20196
20197         // create a local reference to the drag and drop manager
20198         this.DDM = Roo.dd.DDM;
20199         // initialize the groups array
20200         this.groups = {};
20201
20202         // assume that we have an element reference instead of an id if the
20203         // parameter is not a string
20204         if (typeof id !== "string") {
20205             id = Roo.id(id);
20206         }
20207
20208         // set the id
20209         this.id = id;
20210
20211         // add to an interaction group
20212         this.addToGroup((sGroup) ? sGroup : "default");
20213
20214         // We don't want to register this as the handle with the manager
20215         // so we just set the id rather than calling the setter.
20216         this.handleElId = id;
20217
20218         // the linked element is the element that gets dragged by default
20219         this.setDragElId(id);
20220
20221         // by default, clicked anchors will not start drag operations.
20222         this.invalidHandleTypes = { A: "A" };
20223         this.invalidHandleIds = {};
20224         this.invalidHandleClasses = [];
20225
20226         this.applyConfig();
20227
20228         this.handleOnAvailable();
20229     },
20230
20231     /**
20232      * Applies the configuration parameters that were passed into the constructor.
20233      * This is supposed to happen at each level through the inheritance chain.  So
20234      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20235      * DragDrop in order to get all of the parameters that are available in
20236      * each object.
20237      * @method applyConfig
20238      */
20239     applyConfig: function() {
20240
20241         // configurable properties:
20242         //    padding, isTarget, maintainOffset, primaryButtonOnly
20243         this.padding           = this.config.padding || [0, 0, 0, 0];
20244         this.isTarget          = (this.config.isTarget !== false);
20245         this.maintainOffset    = (this.config.maintainOffset);
20246         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20247
20248     },
20249
20250     /**
20251      * Executed when the linked element is available
20252      * @method handleOnAvailable
20253      * @private
20254      */
20255     handleOnAvailable: function() {
20256         this.available = true;
20257         this.resetConstraints();
20258         this.onAvailable();
20259     },
20260
20261      /**
20262      * Configures the padding for the target zone in px.  Effectively expands
20263      * (or reduces) the virtual object size for targeting calculations.
20264      * Supports css-style shorthand; if only one parameter is passed, all sides
20265      * will have that padding, and if only two are passed, the top and bottom
20266      * will have the first param, the left and right the second.
20267      * @method setPadding
20268      * @param {int} iTop    Top pad
20269      * @param {int} iRight  Right pad
20270      * @param {int} iBot    Bot pad
20271      * @param {int} iLeft   Left pad
20272      */
20273     setPadding: function(iTop, iRight, iBot, iLeft) {
20274         // this.padding = [iLeft, iRight, iTop, iBot];
20275         if (!iRight && 0 !== iRight) {
20276             this.padding = [iTop, iTop, iTop, iTop];
20277         } else if (!iBot && 0 !== iBot) {
20278             this.padding = [iTop, iRight, iTop, iRight];
20279         } else {
20280             this.padding = [iTop, iRight, iBot, iLeft];
20281         }
20282     },
20283
20284     /**
20285      * Stores the initial placement of the linked element.
20286      * @method setInitialPosition
20287      * @param {int} diffX   the X offset, default 0
20288      * @param {int} diffY   the Y offset, default 0
20289      */
20290     setInitPosition: function(diffX, diffY) {
20291         var el = this.getEl();
20292
20293         if (!this.DDM.verifyEl(el)) {
20294             return;
20295         }
20296
20297         var dx = diffX || 0;
20298         var dy = diffY || 0;
20299
20300         var p = Dom.getXY( el );
20301
20302         this.initPageX = p[0] - dx;
20303         this.initPageY = p[1] - dy;
20304
20305         this.lastPageX = p[0];
20306         this.lastPageY = p[1];
20307
20308
20309         this.setStartPosition(p);
20310     },
20311
20312     /**
20313      * Sets the start position of the element.  This is set when the obj
20314      * is initialized, the reset when a drag is started.
20315      * @method setStartPosition
20316      * @param pos current position (from previous lookup)
20317      * @private
20318      */
20319     setStartPosition: function(pos) {
20320         var p = pos || Dom.getXY( this.getEl() );
20321         this.deltaSetXY = null;
20322
20323         this.startPageX = p[0];
20324         this.startPageY = p[1];
20325     },
20326
20327     /**
20328      * Add this instance to a group of related drag/drop objects.  All
20329      * instances belong to at least one group, and can belong to as many
20330      * groups as needed.
20331      * @method addToGroup
20332      * @param sGroup {string} the name of the group
20333      */
20334     addToGroup: function(sGroup) {
20335         this.groups[sGroup] = true;
20336         this.DDM.regDragDrop(this, sGroup);
20337     },
20338
20339     /**
20340      * Remove's this instance from the supplied interaction group
20341      * @method removeFromGroup
20342      * @param {string}  sGroup  The group to drop
20343      */
20344     removeFromGroup: function(sGroup) {
20345         if (this.groups[sGroup]) {
20346             delete this.groups[sGroup];
20347         }
20348
20349         this.DDM.removeDDFromGroup(this, sGroup);
20350     },
20351
20352     /**
20353      * Allows you to specify that an element other than the linked element
20354      * will be moved with the cursor during a drag
20355      * @method setDragElId
20356      * @param id {string} the id of the element that will be used to initiate the drag
20357      */
20358     setDragElId: function(id) {
20359         this.dragElId = id;
20360     },
20361
20362     /**
20363      * Allows you to specify a child of the linked element that should be
20364      * used to initiate the drag operation.  An example of this would be if
20365      * you have a content div with text and links.  Clicking anywhere in the
20366      * content area would normally start the drag operation.  Use this method
20367      * to specify that an element inside of the content div is the element
20368      * that starts the drag operation.
20369      * @method setHandleElId
20370      * @param id {string} the id of the element that will be used to
20371      * initiate the drag.
20372      */
20373     setHandleElId: function(id) {
20374         if (typeof id !== "string") {
20375             id = Roo.id(id);
20376         }
20377         this.handleElId = id;
20378         this.DDM.regHandle(this.id, id);
20379     },
20380
20381     /**
20382      * Allows you to set an element outside of the linked element as a drag
20383      * handle
20384      * @method setOuterHandleElId
20385      * @param id the id of the element that will be used to initiate the drag
20386      */
20387     setOuterHandleElId: function(id) {
20388         if (typeof id !== "string") {
20389             id = Roo.id(id);
20390         }
20391         Event.on(id, "mousedown",
20392                 this.handleMouseDown, this);
20393         this.setHandleElId(id);
20394
20395         this.hasOuterHandles = true;
20396     },
20397
20398     /**
20399      * Remove all drag and drop hooks for this element
20400      * @method unreg
20401      */
20402     unreg: function() {
20403         Event.un(this.id, "mousedown",
20404                 this.handleMouseDown);
20405         Event.un(this.id, "touchstart",
20406                 this.handleMouseDown);
20407         this._domRef = null;
20408         this.DDM._remove(this);
20409     },
20410
20411     destroy : function(){
20412         this.unreg();
20413     },
20414
20415     /**
20416      * Returns true if this instance is locked, or the drag drop mgr is locked
20417      * (meaning that all drag/drop is disabled on the page.)
20418      * @method isLocked
20419      * @return {boolean} true if this obj or all drag/drop is locked, else
20420      * false
20421      */
20422     isLocked: function() {
20423         return (this.DDM.isLocked() || this.locked);
20424     },
20425
20426     /**
20427      * Fired when this object is clicked
20428      * @method handleMouseDown
20429      * @param {Event} e
20430      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20431      * @private
20432      */
20433     handleMouseDown: function(e, oDD){
20434      
20435         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20436             //Roo.log('not touch/ button !=0');
20437             return;
20438         }
20439         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20440             return; // double touch..
20441         }
20442         
20443
20444         if (this.isLocked()) {
20445             //Roo.log('locked');
20446             return;
20447         }
20448
20449         this.DDM.refreshCache(this.groups);
20450 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20451         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20452         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20453             //Roo.log('no outer handes or not over target');
20454                 // do nothing.
20455         } else {
20456 //            Roo.log('check validator');
20457             if (this.clickValidator(e)) {
20458 //                Roo.log('validate success');
20459                 // set the initial element position
20460                 this.setStartPosition();
20461
20462
20463                 this.b4MouseDown(e);
20464                 this.onMouseDown(e);
20465
20466                 this.DDM.handleMouseDown(e, this);
20467
20468                 this.DDM.stopEvent(e);
20469             } else {
20470
20471
20472             }
20473         }
20474     },
20475
20476     clickValidator: function(e) {
20477         var target = e.getTarget();
20478         return ( this.isValidHandleChild(target) &&
20479                     (this.id == this.handleElId ||
20480                         this.DDM.handleWasClicked(target, this.id)) );
20481     },
20482
20483     /**
20484      * Allows you to specify a tag name that should not start a drag operation
20485      * when clicked.  This is designed to facilitate embedding links within a
20486      * drag handle that do something other than start the drag.
20487      * @method addInvalidHandleType
20488      * @param {string} tagName the type of element to exclude
20489      */
20490     addInvalidHandleType: function(tagName) {
20491         var type = tagName.toUpperCase();
20492         this.invalidHandleTypes[type] = type;
20493     },
20494
20495     /**
20496      * Lets you to specify an element id for a child of a drag handle
20497      * that should not initiate a drag
20498      * @method addInvalidHandleId
20499      * @param {string} id the element id of the element you wish to ignore
20500      */
20501     addInvalidHandleId: function(id) {
20502         if (typeof id !== "string") {
20503             id = Roo.id(id);
20504         }
20505         this.invalidHandleIds[id] = id;
20506     },
20507
20508     /**
20509      * Lets you specify a css class of elements that will not initiate a drag
20510      * @method addInvalidHandleClass
20511      * @param {string} cssClass the class of the elements you wish to ignore
20512      */
20513     addInvalidHandleClass: function(cssClass) {
20514         this.invalidHandleClasses.push(cssClass);
20515     },
20516
20517     /**
20518      * Unsets an excluded tag name set by addInvalidHandleType
20519      * @method removeInvalidHandleType
20520      * @param {string} tagName the type of element to unexclude
20521      */
20522     removeInvalidHandleType: function(tagName) {
20523         var type = tagName.toUpperCase();
20524         // this.invalidHandleTypes[type] = null;
20525         delete this.invalidHandleTypes[type];
20526     },
20527
20528     /**
20529      * Unsets an invalid handle id
20530      * @method removeInvalidHandleId
20531      * @param {string} id the id of the element to re-enable
20532      */
20533     removeInvalidHandleId: function(id) {
20534         if (typeof id !== "string") {
20535             id = Roo.id(id);
20536         }
20537         delete this.invalidHandleIds[id];
20538     },
20539
20540     /**
20541      * Unsets an invalid css class
20542      * @method removeInvalidHandleClass
20543      * @param {string} cssClass the class of the element(s) you wish to
20544      * re-enable
20545      */
20546     removeInvalidHandleClass: function(cssClass) {
20547         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20548             if (this.invalidHandleClasses[i] == cssClass) {
20549                 delete this.invalidHandleClasses[i];
20550             }
20551         }
20552     },
20553
20554     /**
20555      * Checks the tag exclusion list to see if this click should be ignored
20556      * @method isValidHandleChild
20557      * @param {HTMLElement} node the HTMLElement to evaluate
20558      * @return {boolean} true if this is a valid tag type, false if not
20559      */
20560     isValidHandleChild: function(node) {
20561
20562         var valid = true;
20563         // var n = (node.nodeName == "#text") ? node.parentNode : node;
20564         var nodeName;
20565         try {
20566             nodeName = node.nodeName.toUpperCase();
20567         } catch(e) {
20568             nodeName = node.nodeName;
20569         }
20570         valid = valid && !this.invalidHandleTypes[nodeName];
20571         valid = valid && !this.invalidHandleIds[node.id];
20572
20573         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20574             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20575         }
20576
20577
20578         return valid;
20579
20580     },
20581
20582     /**
20583      * Create the array of horizontal tick marks if an interval was specified
20584      * in setXConstraint().
20585      * @method setXTicks
20586      * @private
20587      */
20588     setXTicks: function(iStartX, iTickSize) {
20589         this.xTicks = [];
20590         this.xTickSize = iTickSize;
20591
20592         var tickMap = {};
20593
20594         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20595             if (!tickMap[i]) {
20596                 this.xTicks[this.xTicks.length] = i;
20597                 tickMap[i] = true;
20598             }
20599         }
20600
20601         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20602             if (!tickMap[i]) {
20603                 this.xTicks[this.xTicks.length] = i;
20604                 tickMap[i] = true;
20605             }
20606         }
20607
20608         this.xTicks.sort(this.DDM.numericSort) ;
20609     },
20610
20611     /**
20612      * Create the array of vertical tick marks if an interval was specified in
20613      * setYConstraint().
20614      * @method setYTicks
20615      * @private
20616      */
20617     setYTicks: function(iStartY, iTickSize) {
20618         this.yTicks = [];
20619         this.yTickSize = iTickSize;
20620
20621         var tickMap = {};
20622
20623         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20624             if (!tickMap[i]) {
20625                 this.yTicks[this.yTicks.length] = i;
20626                 tickMap[i] = true;
20627             }
20628         }
20629
20630         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20631             if (!tickMap[i]) {
20632                 this.yTicks[this.yTicks.length] = i;
20633                 tickMap[i] = true;
20634             }
20635         }
20636
20637         this.yTicks.sort(this.DDM.numericSort) ;
20638     },
20639
20640     /**
20641      * By default, the element can be dragged any place on the screen.  Use
20642      * this method to limit the horizontal travel of the element.  Pass in
20643      * 0,0 for the parameters if you want to lock the drag to the y axis.
20644      * @method setXConstraint
20645      * @param {int} iLeft the number of pixels the element can move to the left
20646      * @param {int} iRight the number of pixels the element can move to the
20647      * right
20648      * @param {int} iTickSize optional parameter for specifying that the
20649      * element
20650      * should move iTickSize pixels at a time.
20651      */
20652     setXConstraint: function(iLeft, iRight, iTickSize) {
20653         this.leftConstraint = iLeft;
20654         this.rightConstraint = iRight;
20655
20656         this.minX = this.initPageX - iLeft;
20657         this.maxX = this.initPageX + iRight;
20658         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20659
20660         this.constrainX = true;
20661     },
20662
20663     /**
20664      * Clears any constraints applied to this instance.  Also clears ticks
20665      * since they can't exist independent of a constraint at this time.
20666      * @method clearConstraints
20667      */
20668     clearConstraints: function() {
20669         this.constrainX = false;
20670         this.constrainY = false;
20671         this.clearTicks();
20672     },
20673
20674     /**
20675      * Clears any tick interval defined for this instance
20676      * @method clearTicks
20677      */
20678     clearTicks: function() {
20679         this.xTicks = null;
20680         this.yTicks = null;
20681         this.xTickSize = 0;
20682         this.yTickSize = 0;
20683     },
20684
20685     /**
20686      * By default, the element can be dragged any place on the screen.  Set
20687      * this to limit the vertical travel of the element.  Pass in 0,0 for the
20688      * parameters if you want to lock the drag to the x axis.
20689      * @method setYConstraint
20690      * @param {int} iUp the number of pixels the element can move up
20691      * @param {int} iDown the number of pixels the element can move down
20692      * @param {int} iTickSize optional parameter for specifying that the
20693      * element should move iTickSize pixels at a time.
20694      */
20695     setYConstraint: function(iUp, iDown, iTickSize) {
20696         this.topConstraint = iUp;
20697         this.bottomConstraint = iDown;
20698
20699         this.minY = this.initPageY - iUp;
20700         this.maxY = this.initPageY + iDown;
20701         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
20702
20703         this.constrainY = true;
20704
20705     },
20706
20707     /**
20708      * resetConstraints must be called if you manually reposition a dd element.
20709      * @method resetConstraints
20710      * @param {boolean} maintainOffset
20711      */
20712     resetConstraints: function() {
20713
20714
20715         // Maintain offsets if necessary
20716         if (this.initPageX || this.initPageX === 0) {
20717             // figure out how much this thing has moved
20718             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
20719             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
20720
20721             this.setInitPosition(dx, dy);
20722
20723         // This is the first time we have detected the element's position
20724         } else {
20725             this.setInitPosition();
20726         }
20727
20728         if (this.constrainX) {
20729             this.setXConstraint( this.leftConstraint,
20730                                  this.rightConstraint,
20731                                  this.xTickSize        );
20732         }
20733
20734         if (this.constrainY) {
20735             this.setYConstraint( this.topConstraint,
20736                                  this.bottomConstraint,
20737                                  this.yTickSize         );
20738         }
20739     },
20740
20741     /**
20742      * Normally the drag element is moved pixel by pixel, but we can specify
20743      * that it move a number of pixels at a time.  This method resolves the
20744      * location when we have it set up like this.
20745      * @method getTick
20746      * @param {int} val where we want to place the object
20747      * @param {int[]} tickArray sorted array of valid points
20748      * @return {int} the closest tick
20749      * @private
20750      */
20751     getTick: function(val, tickArray) {
20752
20753         if (!tickArray) {
20754             // If tick interval is not defined, it is effectively 1 pixel,
20755             // so we return the value passed to us.
20756             return val;
20757         } else if (tickArray[0] >= val) {
20758             // The value is lower than the first tick, so we return the first
20759             // tick.
20760             return tickArray[0];
20761         } else {
20762             for (var i=0, len=tickArray.length; i<len; ++i) {
20763                 var next = i + 1;
20764                 if (tickArray[next] && tickArray[next] >= val) {
20765                     var diff1 = val - tickArray[i];
20766                     var diff2 = tickArray[next] - val;
20767                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
20768                 }
20769             }
20770
20771             // The value is larger than the last tick, so we return the last
20772             // tick.
20773             return tickArray[tickArray.length - 1];
20774         }
20775     },
20776
20777     /**
20778      * toString method
20779      * @method toString
20780      * @return {string} string representation of the dd obj
20781      */
20782     toString: function() {
20783         return ("DragDrop " + this.id);
20784     }
20785
20786 });
20787
20788 })();
20789 /*
20790  * Based on:
20791  * Ext JS Library 1.1.1
20792  * Copyright(c) 2006-2007, Ext JS, LLC.
20793  *
20794  * Originally Released Under LGPL - original licence link has changed is not relivant.
20795  *
20796  * Fork - LGPL
20797  * <script type="text/javascript">
20798  */
20799
20800
20801 /**
20802  * The drag and drop utility provides a framework for building drag and drop
20803  * applications.  In addition to enabling drag and drop for specific elements,
20804  * the drag and drop elements are tracked by the manager class, and the
20805  * interactions between the various elements are tracked during the drag and
20806  * the implementing code is notified about these important moments.
20807  */
20808
20809 // Only load the library once.  Rewriting the manager class would orphan
20810 // existing drag and drop instances.
20811 if (!Roo.dd.DragDropMgr) {
20812
20813 /**
20814  * @class Roo.dd.DragDropMgr
20815  * DragDropMgr is a singleton that tracks the element interaction for
20816  * all DragDrop items in the window.  Generally, you will not call
20817  * this class directly, but it does have helper methods that could
20818  * be useful in your DragDrop implementations.
20819  * @static
20820  */
20821 Roo.dd.DragDropMgr = function() {
20822
20823     var Event = Roo.EventManager;
20824
20825     return {
20826
20827         /**
20828          * Two dimensional Array of registered DragDrop objects.  The first
20829          * dimension is the DragDrop item group, the second the DragDrop
20830          * object.
20831          * @property ids
20832          * @type {string: string}
20833          * @private
20834          * @static
20835          */
20836         ids: {},
20837
20838         /**
20839          * Array of element ids defined as drag handles.  Used to determine
20840          * if the element that generated the mousedown event is actually the
20841          * handle and not the html element itself.
20842          * @property handleIds
20843          * @type {string: string}
20844          * @private
20845          * @static
20846          */
20847         handleIds: {},
20848
20849         /**
20850          * the DragDrop object that is currently being dragged
20851          * @property dragCurrent
20852          * @type DragDrop
20853          * @private
20854          * @static
20855          **/
20856         dragCurrent: null,
20857
20858         /**
20859          * the DragDrop object(s) that are being hovered over
20860          * @property dragOvers
20861          * @type Array
20862          * @private
20863          * @static
20864          */
20865         dragOvers: {},
20866
20867         /**
20868          * the X distance between the cursor and the object being dragged
20869          * @property deltaX
20870          * @type int
20871          * @private
20872          * @static
20873          */
20874         deltaX: 0,
20875
20876         /**
20877          * the Y distance between the cursor and the object being dragged
20878          * @property deltaY
20879          * @type int
20880          * @private
20881          * @static
20882          */
20883         deltaY: 0,
20884
20885         /**
20886          * Flag to determine if we should prevent the default behavior of the
20887          * events we define. By default this is true, but this can be set to
20888          * false if you need the default behavior (not recommended)
20889          * @property preventDefault
20890          * @type boolean
20891          * @static
20892          */
20893         preventDefault: true,
20894
20895         /**
20896          * Flag to determine if we should stop the propagation of the events
20897          * we generate. This is true by default but you may want to set it to
20898          * false if the html element contains other features that require the
20899          * mouse click.
20900          * @property stopPropagation
20901          * @type boolean
20902          * @static
20903          */
20904         stopPropagation: true,
20905
20906         /**
20907          * Internal flag that is set to true when drag and drop has been
20908          * intialized
20909          * @property initialized
20910          * @private
20911          * @static
20912          */
20913         initalized: false,
20914
20915         /**
20916          * All drag and drop can be disabled.
20917          * @property locked
20918          * @private
20919          * @static
20920          */
20921         locked: false,
20922
20923         /**
20924          * Called the first time an element is registered.
20925          * @method init
20926          * @private
20927          * @static
20928          */
20929         init: function() {
20930             this.initialized = true;
20931         },
20932
20933         /**
20934          * In point mode, drag and drop interaction is defined by the
20935          * location of the cursor during the drag/drop
20936          * @property POINT
20937          * @type int
20938          * @static
20939          */
20940         POINT: 0,
20941
20942         /**
20943          * In intersect mode, drag and drop interactio nis defined by the
20944          * overlap of two or more drag and drop objects.
20945          * @property INTERSECT
20946          * @type int
20947          * @static
20948          */
20949         INTERSECT: 1,
20950
20951         /**
20952          * The current drag and drop mode.  Default: POINT
20953          * @property mode
20954          * @type int
20955          * @static
20956          */
20957         mode: 0,
20958
20959         /**
20960          * Runs method on all drag and drop objects
20961          * @method _execOnAll
20962          * @private
20963          * @static
20964          */
20965         _execOnAll: function(sMethod, args) {
20966             for (var i in this.ids) {
20967                 for (var j in this.ids[i]) {
20968                     var oDD = this.ids[i][j];
20969                     if (! this.isTypeOfDD(oDD)) {
20970                         continue;
20971                     }
20972                     oDD[sMethod].apply(oDD, args);
20973                 }
20974             }
20975         },
20976
20977         /**
20978          * Drag and drop initialization.  Sets up the global event handlers
20979          * @method _onLoad
20980          * @private
20981          * @static
20982          */
20983         _onLoad: function() {
20984
20985             this.init();
20986
20987             if (!Roo.isTouch) {
20988                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20989                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20990             }
20991             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20992             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20993             
20994             Event.on(window,   "unload",    this._onUnload, this, true);
20995             Event.on(window,   "resize",    this._onResize, this, true);
20996             // Event.on(window,   "mouseout",    this._test);
20997
20998         },
20999
21000         /**
21001          * Reset constraints on all drag and drop objs
21002          * @method _onResize
21003          * @private
21004          * @static
21005          */
21006         _onResize: function(e) {
21007             this._execOnAll("resetConstraints", []);
21008         },
21009
21010         /**
21011          * Lock all drag and drop functionality
21012          * @method lock
21013          * @static
21014          */
21015         lock: function() { this.locked = true; },
21016
21017         /**
21018          * Unlock all drag and drop functionality
21019          * @method unlock
21020          * @static
21021          */
21022         unlock: function() { this.locked = false; },
21023
21024         /**
21025          * Is drag and drop locked?
21026          * @method isLocked
21027          * @return {boolean} True if drag and drop is locked, false otherwise.
21028          * @static
21029          */
21030         isLocked: function() { return this.locked; },
21031
21032         /**
21033          * Location cache that is set for all drag drop objects when a drag is
21034          * initiated, cleared when the drag is finished.
21035          * @property locationCache
21036          * @private
21037          * @static
21038          */
21039         locationCache: {},
21040
21041         /**
21042          * Set useCache to false if you want to force object the lookup of each
21043          * drag and drop linked element constantly during a drag.
21044          * @property useCache
21045          * @type boolean
21046          * @static
21047          */
21048         useCache: true,
21049
21050         /**
21051          * The number of pixels that the mouse needs to move after the
21052          * mousedown before the drag is initiated.  Default=3;
21053          * @property clickPixelThresh
21054          * @type int
21055          * @static
21056          */
21057         clickPixelThresh: 3,
21058
21059         /**
21060          * The number of milliseconds after the mousedown event to initiate the
21061          * drag if we don't get a mouseup event. Default=1000
21062          * @property clickTimeThresh
21063          * @type int
21064          * @static
21065          */
21066         clickTimeThresh: 350,
21067
21068         /**
21069          * Flag that indicates that either the drag pixel threshold or the
21070          * mousdown time threshold has been met
21071          * @property dragThreshMet
21072          * @type boolean
21073          * @private
21074          * @static
21075          */
21076         dragThreshMet: false,
21077
21078         /**
21079          * Timeout used for the click time threshold
21080          * @property clickTimeout
21081          * @type Object
21082          * @private
21083          * @static
21084          */
21085         clickTimeout: null,
21086
21087         /**
21088          * The X position of the mousedown event stored for later use when a
21089          * drag threshold is met.
21090          * @property startX
21091          * @type int
21092          * @private
21093          * @static
21094          */
21095         startX: 0,
21096
21097         /**
21098          * The Y position of the mousedown event stored for later use when a
21099          * drag threshold is met.
21100          * @property startY
21101          * @type int
21102          * @private
21103          * @static
21104          */
21105         startY: 0,
21106
21107         /**
21108          * Each DragDrop instance must be registered with the DragDropMgr.
21109          * This is executed in DragDrop.init()
21110          * @method regDragDrop
21111          * @param {DragDrop} oDD the DragDrop object to register
21112          * @param {String} sGroup the name of the group this element belongs to
21113          * @static
21114          */
21115         regDragDrop: function(oDD, sGroup) {
21116             if (!this.initialized) { this.init(); }
21117
21118             if (!this.ids[sGroup]) {
21119                 this.ids[sGroup] = {};
21120             }
21121             this.ids[sGroup][oDD.id] = oDD;
21122         },
21123
21124         /**
21125          * Removes the supplied dd instance from the supplied group. Executed
21126          * by DragDrop.removeFromGroup, so don't call this function directly.
21127          * @method removeDDFromGroup
21128          * @private
21129          * @static
21130          */
21131         removeDDFromGroup: function(oDD, sGroup) {
21132             if (!this.ids[sGroup]) {
21133                 this.ids[sGroup] = {};
21134             }
21135
21136             var obj = this.ids[sGroup];
21137             if (obj && obj[oDD.id]) {
21138                 delete obj[oDD.id];
21139             }
21140         },
21141
21142         /**
21143          * Unregisters a drag and drop item.  This is executed in
21144          * DragDrop.unreg, use that method instead of calling this directly.
21145          * @method _remove
21146          * @private
21147          * @static
21148          */
21149         _remove: function(oDD) {
21150             for (var g in oDD.groups) {
21151                 if (g && this.ids[g][oDD.id]) {
21152                     delete this.ids[g][oDD.id];
21153                 }
21154             }
21155             delete this.handleIds[oDD.id];
21156         },
21157
21158         /**
21159          * Each DragDrop handle element must be registered.  This is done
21160          * automatically when executing DragDrop.setHandleElId()
21161          * @method regHandle
21162          * @param {String} sDDId the DragDrop id this element is a handle for
21163          * @param {String} sHandleId the id of the element that is the drag
21164          * handle
21165          * @static
21166          */
21167         regHandle: function(sDDId, sHandleId) {
21168             if (!this.handleIds[sDDId]) {
21169                 this.handleIds[sDDId] = {};
21170             }
21171             this.handleIds[sDDId][sHandleId] = sHandleId;
21172         },
21173
21174         /**
21175          * Utility function to determine if a given element has been
21176          * registered as a drag drop item.
21177          * @method isDragDrop
21178          * @param {String} id the element id to check
21179          * @return {boolean} true if this element is a DragDrop item,
21180          * false otherwise
21181          * @static
21182          */
21183         isDragDrop: function(id) {
21184             return ( this.getDDById(id) ) ? true : false;
21185         },
21186
21187         /**
21188          * Returns the drag and drop instances that are in all groups the
21189          * passed in instance belongs to.
21190          * @method getRelated
21191          * @param {DragDrop} p_oDD the obj to get related data for
21192          * @param {boolean} bTargetsOnly if true, only return targetable objs
21193          * @return {DragDrop[]} the related instances
21194          * @static
21195          */
21196         getRelated: function(p_oDD, bTargetsOnly) {
21197             var oDDs = [];
21198             for (var i in p_oDD.groups) {
21199                 for (j in this.ids[i]) {
21200                     var dd = this.ids[i][j];
21201                     if (! this.isTypeOfDD(dd)) {
21202                         continue;
21203                     }
21204                     if (!bTargetsOnly || dd.isTarget) {
21205                         oDDs[oDDs.length] = dd;
21206                     }
21207                 }
21208             }
21209
21210             return oDDs;
21211         },
21212
21213         /**
21214          * Returns true if the specified dd target is a legal target for
21215          * the specifice drag obj
21216          * @method isLegalTarget
21217          * @param {DragDrop} the drag obj
21218          * @param {DragDrop} the target
21219          * @return {boolean} true if the target is a legal target for the
21220          * dd obj
21221          * @static
21222          */
21223         isLegalTarget: function (oDD, oTargetDD) {
21224             var targets = this.getRelated(oDD, true);
21225             for (var i=0, len=targets.length;i<len;++i) {
21226                 if (targets[i].id == oTargetDD.id) {
21227                     return true;
21228                 }
21229             }
21230
21231             return false;
21232         },
21233
21234         /**
21235          * My goal is to be able to transparently determine if an object is
21236          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21237          * returns "object", oDD.constructor.toString() always returns
21238          * "DragDrop" and not the name of the subclass.  So for now it just
21239          * evaluates a well-known variable in DragDrop.
21240          * @method isTypeOfDD
21241          * @param {Object} the object to evaluate
21242          * @return {boolean} true if typeof oDD = DragDrop
21243          * @static
21244          */
21245         isTypeOfDD: function (oDD) {
21246             return (oDD && oDD.__ygDragDrop);
21247         },
21248
21249         /**
21250          * Utility function to determine if a given element has been
21251          * registered as a drag drop handle for the given Drag Drop object.
21252          * @method isHandle
21253          * @param {String} id the element id to check
21254          * @return {boolean} true if this element is a DragDrop handle, false
21255          * otherwise
21256          * @static
21257          */
21258         isHandle: function(sDDId, sHandleId) {
21259             return ( this.handleIds[sDDId] &&
21260                             this.handleIds[sDDId][sHandleId] );
21261         },
21262
21263         /**
21264          * Returns the DragDrop instance for a given id
21265          * @method getDDById
21266          * @param {String} id the id of the DragDrop object
21267          * @return {DragDrop} the drag drop object, null if it is not found
21268          * @static
21269          */
21270         getDDById: function(id) {
21271             for (var i in this.ids) {
21272                 if (this.ids[i][id]) {
21273                     return this.ids[i][id];
21274                 }
21275             }
21276             return null;
21277         },
21278
21279         /**
21280          * Fired after a registered DragDrop object gets the mousedown event.
21281          * Sets up the events required to track the object being dragged
21282          * @method handleMouseDown
21283          * @param {Event} e the event
21284          * @param oDD the DragDrop object being dragged
21285          * @private
21286          * @static
21287          */
21288         handleMouseDown: function(e, oDD) {
21289             if(Roo.QuickTips){
21290                 Roo.QuickTips.disable();
21291             }
21292             this.currentTarget = e.getTarget();
21293
21294             this.dragCurrent = oDD;
21295
21296             var el = oDD.getEl();
21297
21298             // track start position
21299             this.startX = e.getPageX();
21300             this.startY = e.getPageY();
21301
21302             this.deltaX = this.startX - el.offsetLeft;
21303             this.deltaY = this.startY - el.offsetTop;
21304
21305             this.dragThreshMet = false;
21306
21307             this.clickTimeout = setTimeout(
21308                     function() {
21309                         var DDM = Roo.dd.DDM;
21310                         DDM.startDrag(DDM.startX, DDM.startY);
21311                     },
21312                     this.clickTimeThresh );
21313         },
21314
21315         /**
21316          * Fired when either the drag pixel threshol or the mousedown hold
21317          * time threshold has been met.
21318          * @method startDrag
21319          * @param x {int} the X position of the original mousedown
21320          * @param y {int} the Y position of the original mousedown
21321          * @static
21322          */
21323         startDrag: function(x, y) {
21324             clearTimeout(this.clickTimeout);
21325             if (this.dragCurrent) {
21326                 this.dragCurrent.b4StartDrag(x, y);
21327                 this.dragCurrent.startDrag(x, y);
21328             }
21329             this.dragThreshMet = true;
21330         },
21331
21332         /**
21333          * Internal function to handle the mouseup event.  Will be invoked
21334          * from the context of the document.
21335          * @method handleMouseUp
21336          * @param {Event} e the event
21337          * @private
21338          * @static
21339          */
21340         handleMouseUp: function(e) {
21341
21342             if(Roo.QuickTips){
21343                 Roo.QuickTips.enable();
21344             }
21345             if (! this.dragCurrent) {
21346                 return;
21347             }
21348
21349             clearTimeout(this.clickTimeout);
21350
21351             if (this.dragThreshMet) {
21352                 this.fireEvents(e, true);
21353             } else {
21354             }
21355
21356             this.stopDrag(e);
21357
21358             this.stopEvent(e);
21359         },
21360
21361         /**
21362          * Utility to stop event propagation and event default, if these
21363          * features are turned on.
21364          * @method stopEvent
21365          * @param {Event} e the event as returned by this.getEvent()
21366          * @static
21367          */
21368         stopEvent: function(e){
21369             if(this.stopPropagation) {
21370                 e.stopPropagation();
21371             }
21372
21373             if (this.preventDefault) {
21374                 e.preventDefault();
21375             }
21376         },
21377
21378         /**
21379          * Internal function to clean up event handlers after the drag
21380          * operation is complete
21381          * @method stopDrag
21382          * @param {Event} e the event
21383          * @private
21384          * @static
21385          */
21386         stopDrag: function(e) {
21387             // Fire the drag end event for the item that was dragged
21388             if (this.dragCurrent) {
21389                 if (this.dragThreshMet) {
21390                     this.dragCurrent.b4EndDrag(e);
21391                     this.dragCurrent.endDrag(e);
21392                 }
21393
21394                 this.dragCurrent.onMouseUp(e);
21395             }
21396
21397             this.dragCurrent = null;
21398             this.dragOvers = {};
21399         },
21400
21401         /**
21402          * Internal function to handle the mousemove event.  Will be invoked
21403          * from the context of the html element.
21404          *
21405          * @TODO figure out what we can do about mouse events lost when the
21406          * user drags objects beyond the window boundary.  Currently we can
21407          * detect this in internet explorer by verifying that the mouse is
21408          * down during the mousemove event.  Firefox doesn't give us the
21409          * button state on the mousemove event.
21410          * @method handleMouseMove
21411          * @param {Event} e the event
21412          * @private
21413          * @static
21414          */
21415         handleMouseMove: function(e) {
21416             if (! this.dragCurrent) {
21417                 return true;
21418             }
21419
21420             // var button = e.which || e.button;
21421
21422             // check for IE mouseup outside of page boundary
21423             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21424                 this.stopEvent(e);
21425                 return this.handleMouseUp(e);
21426             }
21427
21428             if (!this.dragThreshMet) {
21429                 var diffX = Math.abs(this.startX - e.getPageX());
21430                 var diffY = Math.abs(this.startY - e.getPageY());
21431                 if (diffX > this.clickPixelThresh ||
21432                             diffY > this.clickPixelThresh) {
21433                     this.startDrag(this.startX, this.startY);
21434                 }
21435             }
21436
21437             if (this.dragThreshMet) {
21438                 this.dragCurrent.b4Drag(e);
21439                 this.dragCurrent.onDrag(e);
21440                 if(!this.dragCurrent.moveOnly){
21441                     this.fireEvents(e, false);
21442                 }
21443             }
21444
21445             this.stopEvent(e);
21446
21447             return true;
21448         },
21449
21450         /**
21451          * Iterates over all of the DragDrop elements to find ones we are
21452          * hovering over or dropping on
21453          * @method fireEvents
21454          * @param {Event} e the event
21455          * @param {boolean} isDrop is this a drop op or a mouseover op?
21456          * @private
21457          * @static
21458          */
21459         fireEvents: function(e, isDrop) {
21460             var dc = this.dragCurrent;
21461
21462             // If the user did the mouse up outside of the window, we could
21463             // get here even though we have ended the drag.
21464             if (!dc || dc.isLocked()) {
21465                 return;
21466             }
21467
21468             var pt = e.getPoint();
21469
21470             // cache the previous dragOver array
21471             var oldOvers = [];
21472
21473             var outEvts   = [];
21474             var overEvts  = [];
21475             var dropEvts  = [];
21476             var enterEvts = [];
21477
21478             // Check to see if the object(s) we were hovering over is no longer
21479             // being hovered over so we can fire the onDragOut event
21480             for (var i in this.dragOvers) {
21481
21482                 var ddo = this.dragOvers[i];
21483
21484                 if (! this.isTypeOfDD(ddo)) {
21485                     continue;
21486                 }
21487
21488                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21489                     outEvts.push( ddo );
21490                 }
21491
21492                 oldOvers[i] = true;
21493                 delete this.dragOvers[i];
21494             }
21495
21496             for (var sGroup in dc.groups) {
21497
21498                 if ("string" != typeof sGroup) {
21499                     continue;
21500                 }
21501
21502                 for (i in this.ids[sGroup]) {
21503                     var oDD = this.ids[sGroup][i];
21504                     if (! this.isTypeOfDD(oDD)) {
21505                         continue;
21506                     }
21507
21508                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21509                         if (this.isOverTarget(pt, oDD, this.mode)) {
21510                             // look for drop interactions
21511                             if (isDrop) {
21512                                 dropEvts.push( oDD );
21513                             // look for drag enter and drag over interactions
21514                             } else {
21515
21516                                 // initial drag over: dragEnter fires
21517                                 if (!oldOvers[oDD.id]) {
21518                                     enterEvts.push( oDD );
21519                                 // subsequent drag overs: dragOver fires
21520                                 } else {
21521                                     overEvts.push( oDD );
21522                                 }
21523
21524                                 this.dragOvers[oDD.id] = oDD;
21525                             }
21526                         }
21527                     }
21528                 }
21529             }
21530
21531             if (this.mode) {
21532                 if (outEvts.length) {
21533                     dc.b4DragOut(e, outEvts);
21534                     dc.onDragOut(e, outEvts);
21535                 }
21536
21537                 if (enterEvts.length) {
21538                     dc.onDragEnter(e, enterEvts);
21539                 }
21540
21541                 if (overEvts.length) {
21542                     dc.b4DragOver(e, overEvts);
21543                     dc.onDragOver(e, overEvts);
21544                 }
21545
21546                 if (dropEvts.length) {
21547                     dc.b4DragDrop(e, dropEvts);
21548                     dc.onDragDrop(e, dropEvts);
21549                 }
21550
21551             } else {
21552                 // fire dragout events
21553                 var len = 0;
21554                 for (i=0, len=outEvts.length; i<len; ++i) {
21555                     dc.b4DragOut(e, outEvts[i].id);
21556                     dc.onDragOut(e, outEvts[i].id);
21557                 }
21558
21559                 // fire enter events
21560                 for (i=0,len=enterEvts.length; i<len; ++i) {
21561                     // dc.b4DragEnter(e, oDD.id);
21562                     dc.onDragEnter(e, enterEvts[i].id);
21563                 }
21564
21565                 // fire over events
21566                 for (i=0,len=overEvts.length; i<len; ++i) {
21567                     dc.b4DragOver(e, overEvts[i].id);
21568                     dc.onDragOver(e, overEvts[i].id);
21569                 }
21570
21571                 // fire drop events
21572                 for (i=0, len=dropEvts.length; i<len; ++i) {
21573                     dc.b4DragDrop(e, dropEvts[i].id);
21574                     dc.onDragDrop(e, dropEvts[i].id);
21575                 }
21576
21577             }
21578
21579             // notify about a drop that did not find a target
21580             if (isDrop && !dropEvts.length) {
21581                 dc.onInvalidDrop(e);
21582             }
21583
21584         },
21585
21586         /**
21587          * Helper function for getting the best match from the list of drag
21588          * and drop objects returned by the drag and drop events when we are
21589          * in INTERSECT mode.  It returns either the first object that the
21590          * cursor is over, or the object that has the greatest overlap with
21591          * the dragged element.
21592          * @method getBestMatch
21593          * @param  {DragDrop[]} dds The array of drag and drop objects
21594          * targeted
21595          * @return {DragDrop}       The best single match
21596          * @static
21597          */
21598         getBestMatch: function(dds) {
21599             var winner = null;
21600             // Return null if the input is not what we expect
21601             //if (!dds || !dds.length || dds.length == 0) {
21602                // winner = null;
21603             // If there is only one item, it wins
21604             //} else if (dds.length == 1) {
21605
21606             var len = dds.length;
21607
21608             if (len == 1) {
21609                 winner = dds[0];
21610             } else {
21611                 // Loop through the targeted items
21612                 for (var i=0; i<len; ++i) {
21613                     var dd = dds[i];
21614                     // If the cursor is over the object, it wins.  If the
21615                     // cursor is over multiple matches, the first one we come
21616                     // to wins.
21617                     if (dd.cursorIsOver) {
21618                         winner = dd;
21619                         break;
21620                     // Otherwise the object with the most overlap wins
21621                     } else {
21622                         if (!winner ||
21623                             winner.overlap.getArea() < dd.overlap.getArea()) {
21624                             winner = dd;
21625                         }
21626                     }
21627                 }
21628             }
21629
21630             return winner;
21631         },
21632
21633         /**
21634          * Refreshes the cache of the top-left and bottom-right points of the
21635          * drag and drop objects in the specified group(s).  This is in the
21636          * format that is stored in the drag and drop instance, so typical
21637          * usage is:
21638          * <code>
21639          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21640          * </code>
21641          * Alternatively:
21642          * <code>
21643          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21644          * </code>
21645          * @TODO this really should be an indexed array.  Alternatively this
21646          * method could accept both.
21647          * @method refreshCache
21648          * @param {Object} groups an associative array of groups to refresh
21649          * @static
21650          */
21651         refreshCache: function(groups) {
21652             for (var sGroup in groups) {
21653                 if ("string" != typeof sGroup) {
21654                     continue;
21655                 }
21656                 for (var i in this.ids[sGroup]) {
21657                     var oDD = this.ids[sGroup][i];
21658
21659                     if (this.isTypeOfDD(oDD)) {
21660                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21661                         var loc = this.getLocation(oDD);
21662                         if (loc) {
21663                             this.locationCache[oDD.id] = loc;
21664                         } else {
21665                             delete this.locationCache[oDD.id];
21666                             // this will unregister the drag and drop object if
21667                             // the element is not in a usable state
21668                             // oDD.unreg();
21669                         }
21670                     }
21671                 }
21672             }
21673         },
21674
21675         /**
21676          * This checks to make sure an element exists and is in the DOM.  The
21677          * main purpose is to handle cases where innerHTML is used to remove
21678          * drag and drop objects from the DOM.  IE provides an 'unspecified
21679          * error' when trying to access the offsetParent of such an element
21680          * @method verifyEl
21681          * @param {HTMLElement} el the element to check
21682          * @return {boolean} true if the element looks usable
21683          * @static
21684          */
21685         verifyEl: function(el) {
21686             if (el) {
21687                 var parent;
21688                 if(Roo.isIE){
21689                     try{
21690                         parent = el.offsetParent;
21691                     }catch(e){}
21692                 }else{
21693                     parent = el.offsetParent;
21694                 }
21695                 if (parent) {
21696                     return true;
21697                 }
21698             }
21699
21700             return false;
21701         },
21702
21703         /**
21704          * Returns a Region object containing the drag and drop element's position
21705          * and size, including the padding configured for it
21706          * @method getLocation
21707          * @param {DragDrop} oDD the drag and drop object to get the
21708          *                       location for
21709          * @return {Roo.lib.Region} a Region object representing the total area
21710          *                             the element occupies, including any padding
21711          *                             the instance is configured for.
21712          * @static
21713          */
21714         getLocation: function(oDD) {
21715             if (! this.isTypeOfDD(oDD)) {
21716                 return null;
21717             }
21718
21719             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
21720
21721             try {
21722                 pos= Roo.lib.Dom.getXY(el);
21723             } catch (e) { }
21724
21725             if (!pos) {
21726                 return null;
21727             }
21728
21729             x1 = pos[0];
21730             x2 = x1 + el.offsetWidth;
21731             y1 = pos[1];
21732             y2 = y1 + el.offsetHeight;
21733
21734             t = y1 - oDD.padding[0];
21735             r = x2 + oDD.padding[1];
21736             b = y2 + oDD.padding[2];
21737             l = x1 - oDD.padding[3];
21738
21739             return new Roo.lib.Region( t, r, b, l );
21740         },
21741
21742         /**
21743          * Checks the cursor location to see if it over the target
21744          * @method isOverTarget
21745          * @param {Roo.lib.Point} pt The point to evaluate
21746          * @param {DragDrop} oTarget the DragDrop object we are inspecting
21747          * @return {boolean} true if the mouse is over the target
21748          * @private
21749          * @static
21750          */
21751         isOverTarget: function(pt, oTarget, intersect) {
21752             // use cache if available
21753             var loc = this.locationCache[oTarget.id];
21754             if (!loc || !this.useCache) {
21755                 loc = this.getLocation(oTarget);
21756                 this.locationCache[oTarget.id] = loc;
21757
21758             }
21759
21760             if (!loc) {
21761                 return false;
21762             }
21763
21764             oTarget.cursorIsOver = loc.contains( pt );
21765
21766             // DragDrop is using this as a sanity check for the initial mousedown
21767             // in this case we are done.  In POINT mode, if the drag obj has no
21768             // contraints, we are also done. Otherwise we need to evaluate the
21769             // location of the target as related to the actual location of the
21770             // dragged element.
21771             var dc = this.dragCurrent;
21772             if (!dc || !dc.getTargetCoord ||
21773                     (!intersect && !dc.constrainX && !dc.constrainY)) {
21774                 return oTarget.cursorIsOver;
21775             }
21776
21777             oTarget.overlap = null;
21778
21779             // Get the current location of the drag element, this is the
21780             // location of the mouse event less the delta that represents
21781             // where the original mousedown happened on the element.  We
21782             // need to consider constraints and ticks as well.
21783             var pos = dc.getTargetCoord(pt.x, pt.y);
21784
21785             var el = dc.getDragEl();
21786             var curRegion = new Roo.lib.Region( pos.y,
21787                                                    pos.x + el.offsetWidth,
21788                                                    pos.y + el.offsetHeight,
21789                                                    pos.x );
21790
21791             var overlap = curRegion.intersect(loc);
21792
21793             if (overlap) {
21794                 oTarget.overlap = overlap;
21795                 return (intersect) ? true : oTarget.cursorIsOver;
21796             } else {
21797                 return false;
21798             }
21799         },
21800
21801         /**
21802          * unload event handler
21803          * @method _onUnload
21804          * @private
21805          * @static
21806          */
21807         _onUnload: function(e, me) {
21808             Roo.dd.DragDropMgr.unregAll();
21809         },
21810
21811         /**
21812          * Cleans up the drag and drop events and objects.
21813          * @method unregAll
21814          * @private
21815          * @static
21816          */
21817         unregAll: function() {
21818
21819             if (this.dragCurrent) {
21820                 this.stopDrag();
21821                 this.dragCurrent = null;
21822             }
21823
21824             this._execOnAll("unreg", []);
21825
21826             for (i in this.elementCache) {
21827                 delete this.elementCache[i];
21828             }
21829
21830             this.elementCache = {};
21831             this.ids = {};
21832         },
21833
21834         /**
21835          * A cache of DOM elements
21836          * @property elementCache
21837          * @private
21838          * @static
21839          */
21840         elementCache: {},
21841
21842         /**
21843          * Get the wrapper for the DOM element specified
21844          * @method getElWrapper
21845          * @param {String} id the id of the element to get
21846          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
21847          * @private
21848          * @deprecated This wrapper isn't that useful
21849          * @static
21850          */
21851         getElWrapper: function(id) {
21852             var oWrapper = this.elementCache[id];
21853             if (!oWrapper || !oWrapper.el) {
21854                 oWrapper = this.elementCache[id] =
21855                     new this.ElementWrapper(Roo.getDom(id));
21856             }
21857             return oWrapper;
21858         },
21859
21860         /**
21861          * Returns the actual DOM element
21862          * @method getElement
21863          * @param {String} id the id of the elment to get
21864          * @return {Object} The element
21865          * @deprecated use Roo.getDom instead
21866          * @static
21867          */
21868         getElement: function(id) {
21869             return Roo.getDom(id);
21870         },
21871
21872         /**
21873          * Returns the style property for the DOM element (i.e.,
21874          * document.getElById(id).style)
21875          * @method getCss
21876          * @param {String} id the id of the elment to get
21877          * @return {Object} The style property of the element
21878          * @deprecated use Roo.getDom instead
21879          * @static
21880          */
21881         getCss: function(id) {
21882             var el = Roo.getDom(id);
21883             return (el) ? el.style : null;
21884         },
21885
21886         /**
21887          * Inner class for cached elements
21888          * @class DragDropMgr.ElementWrapper
21889          * @for DragDropMgr
21890          * @private
21891          * @deprecated
21892          */
21893         ElementWrapper: function(el) {
21894                 /**
21895                  * The element
21896                  * @property el
21897                  */
21898                 this.el = el || null;
21899                 /**
21900                  * The element id
21901                  * @property id
21902                  */
21903                 this.id = this.el && el.id;
21904                 /**
21905                  * A reference to the style property
21906                  * @property css
21907                  */
21908                 this.css = this.el && el.style;
21909             },
21910
21911         /**
21912          * Returns the X position of an html element
21913          * @method getPosX
21914          * @param el the element for which to get the position
21915          * @return {int} the X coordinate
21916          * @for DragDropMgr
21917          * @deprecated use Roo.lib.Dom.getX instead
21918          * @static
21919          */
21920         getPosX: function(el) {
21921             return Roo.lib.Dom.getX(el);
21922         },
21923
21924         /**
21925          * Returns the Y position of an html element
21926          * @method getPosY
21927          * @param el the element for which to get the position
21928          * @return {int} the Y coordinate
21929          * @deprecated use Roo.lib.Dom.getY instead
21930          * @static
21931          */
21932         getPosY: function(el) {
21933             return Roo.lib.Dom.getY(el);
21934         },
21935
21936         /**
21937          * Swap two nodes.  In IE, we use the native method, for others we
21938          * emulate the IE behavior
21939          * @method swapNode
21940          * @param n1 the first node to swap
21941          * @param n2 the other node to swap
21942          * @static
21943          */
21944         swapNode: function(n1, n2) {
21945             if (n1.swapNode) {
21946                 n1.swapNode(n2);
21947             } else {
21948                 var p = n2.parentNode;
21949                 var s = n2.nextSibling;
21950
21951                 if (s == n1) {
21952                     p.insertBefore(n1, n2);
21953                 } else if (n2 == n1.nextSibling) {
21954                     p.insertBefore(n2, n1);
21955                 } else {
21956                     n1.parentNode.replaceChild(n2, n1);
21957                     p.insertBefore(n1, s);
21958                 }
21959             }
21960         },
21961
21962         /**
21963          * Returns the current scroll position
21964          * @method getScroll
21965          * @private
21966          * @static
21967          */
21968         getScroll: function () {
21969             var t, l, dde=document.documentElement, db=document.body;
21970             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21971                 t = dde.scrollTop;
21972                 l = dde.scrollLeft;
21973             } else if (db) {
21974                 t = db.scrollTop;
21975                 l = db.scrollLeft;
21976             } else {
21977
21978             }
21979             return { top: t, left: l };
21980         },
21981
21982         /**
21983          * Returns the specified element style property
21984          * @method getStyle
21985          * @param {HTMLElement} el          the element
21986          * @param {string}      styleProp   the style property
21987          * @return {string} The value of the style property
21988          * @deprecated use Roo.lib.Dom.getStyle
21989          * @static
21990          */
21991         getStyle: function(el, styleProp) {
21992             return Roo.fly(el).getStyle(styleProp);
21993         },
21994
21995         /**
21996          * Gets the scrollTop
21997          * @method getScrollTop
21998          * @return {int} the document's scrollTop
21999          * @static
22000          */
22001         getScrollTop: function () { return this.getScroll().top; },
22002
22003         /**
22004          * Gets the scrollLeft
22005          * @method getScrollLeft
22006          * @return {int} the document's scrollTop
22007          * @static
22008          */
22009         getScrollLeft: function () { return this.getScroll().left; },
22010
22011         /**
22012          * Sets the x/y position of an element to the location of the
22013          * target element.
22014          * @method moveToEl
22015          * @param {HTMLElement} moveEl      The element to move
22016          * @param {HTMLElement} targetEl    The position reference element
22017          * @static
22018          */
22019         moveToEl: function (moveEl, targetEl) {
22020             var aCoord = Roo.lib.Dom.getXY(targetEl);
22021             Roo.lib.Dom.setXY(moveEl, aCoord);
22022         },
22023
22024         /**
22025          * Numeric array sort function
22026          * @method numericSort
22027          * @static
22028          */
22029         numericSort: function(a, b) { return (a - b); },
22030
22031         /**
22032          * Internal counter
22033          * @property _timeoutCount
22034          * @private
22035          * @static
22036          */
22037         _timeoutCount: 0,
22038
22039         /**
22040          * Trying to make the load order less important.  Without this we get
22041          * an error if this file is loaded before the Event Utility.
22042          * @method _addListeners
22043          * @private
22044          * @static
22045          */
22046         _addListeners: function() {
22047             var DDM = Roo.dd.DDM;
22048             if ( Roo.lib.Event && document ) {
22049                 DDM._onLoad();
22050             } else {
22051                 if (DDM._timeoutCount > 2000) {
22052                 } else {
22053                     setTimeout(DDM._addListeners, 10);
22054                     if (document && document.body) {
22055                         DDM._timeoutCount += 1;
22056                     }
22057                 }
22058             }
22059         },
22060
22061         /**
22062          * Recursively searches the immediate parent and all child nodes for
22063          * the handle element in order to determine wheter or not it was
22064          * clicked.
22065          * @method handleWasClicked
22066          * @param node the html element to inspect
22067          * @static
22068          */
22069         handleWasClicked: function(node, id) {
22070             if (this.isHandle(id, node.id)) {
22071                 return true;
22072             } else {
22073                 // check to see if this is a text node child of the one we want
22074                 var p = node.parentNode;
22075
22076                 while (p) {
22077                     if (this.isHandle(id, p.id)) {
22078                         return true;
22079                     } else {
22080                         p = p.parentNode;
22081                     }
22082                 }
22083             }
22084
22085             return false;
22086         }
22087
22088     };
22089
22090 }();
22091
22092 // shorter alias, save a few bytes
22093 Roo.dd.DDM = Roo.dd.DragDropMgr;
22094 Roo.dd.DDM._addListeners();
22095
22096 }/*
22097  * Based on:
22098  * Ext JS Library 1.1.1
22099  * Copyright(c) 2006-2007, Ext JS, LLC.
22100  *
22101  * Originally Released Under LGPL - original licence link has changed is not relivant.
22102  *
22103  * Fork - LGPL
22104  * <script type="text/javascript">
22105  */
22106
22107 /**
22108  * @class Roo.dd.DD
22109  * A DragDrop implementation where the linked element follows the
22110  * mouse cursor during a drag.
22111  * @extends Roo.dd.DragDrop
22112  * @constructor
22113  * @param {String} id the id of the linked element
22114  * @param {String} sGroup the group of related DragDrop items
22115  * @param {object} config an object containing configurable attributes
22116  *                Valid properties for DD:
22117  *                    scroll
22118  */
22119 Roo.dd.DD = function(id, sGroup, config) {
22120     if (id) {
22121         this.init(id, sGroup, config);
22122     }
22123 };
22124
22125 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22126
22127     /**
22128      * When set to true, the utility automatically tries to scroll the browser
22129      * window wehn a drag and drop element is dragged near the viewport boundary.
22130      * Defaults to true.
22131      * @property scroll
22132      * @type boolean
22133      */
22134     scroll: true,
22135
22136     /**
22137      * Sets the pointer offset to the distance between the linked element's top
22138      * left corner and the location the element was clicked
22139      * @method autoOffset
22140      * @param {int} iPageX the X coordinate of the click
22141      * @param {int} iPageY the Y coordinate of the click
22142      */
22143     autoOffset: function(iPageX, iPageY) {
22144         var x = iPageX - this.startPageX;
22145         var y = iPageY - this.startPageY;
22146         this.setDelta(x, y);
22147     },
22148
22149     /**
22150      * Sets the pointer offset.  You can call this directly to force the
22151      * offset to be in a particular location (e.g., pass in 0,0 to set it
22152      * to the center of the object)
22153      * @method setDelta
22154      * @param {int} iDeltaX the distance from the left
22155      * @param {int} iDeltaY the distance from the top
22156      */
22157     setDelta: function(iDeltaX, iDeltaY) {
22158         this.deltaX = iDeltaX;
22159         this.deltaY = iDeltaY;
22160     },
22161
22162     /**
22163      * Sets the drag element to the location of the mousedown or click event,
22164      * maintaining the cursor location relative to the location on the element
22165      * that was clicked.  Override this if you want to place the element in a
22166      * location other than where the cursor is.
22167      * @method setDragElPos
22168      * @param {int} iPageX the X coordinate of the mousedown or drag event
22169      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22170      */
22171     setDragElPos: function(iPageX, iPageY) {
22172         // the first time we do this, we are going to check to make sure
22173         // the element has css positioning
22174
22175         var el = this.getDragEl();
22176         this.alignElWithMouse(el, iPageX, iPageY);
22177     },
22178
22179     /**
22180      * Sets the element to the location of the mousedown or click event,
22181      * maintaining the cursor location relative to the location on the element
22182      * that was clicked.  Override this if you want to place the element in a
22183      * location other than where the cursor is.
22184      * @method alignElWithMouse
22185      * @param {HTMLElement} el the element to move
22186      * @param {int} iPageX the X coordinate of the mousedown or drag event
22187      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22188      */
22189     alignElWithMouse: function(el, iPageX, iPageY) {
22190         var oCoord = this.getTargetCoord(iPageX, iPageY);
22191         var fly = el.dom ? el : Roo.fly(el);
22192         if (!this.deltaSetXY) {
22193             var aCoord = [oCoord.x, oCoord.y];
22194             fly.setXY(aCoord);
22195             var newLeft = fly.getLeft(true);
22196             var newTop  = fly.getTop(true);
22197             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22198         } else {
22199             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22200         }
22201
22202         this.cachePosition(oCoord.x, oCoord.y);
22203         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22204         return oCoord;
22205     },
22206
22207     /**
22208      * Saves the most recent position so that we can reset the constraints and
22209      * tick marks on-demand.  We need to know this so that we can calculate the
22210      * number of pixels the element is offset from its original position.
22211      * @method cachePosition
22212      * @param iPageX the current x position (optional, this just makes it so we
22213      * don't have to look it up again)
22214      * @param iPageY the current y position (optional, this just makes it so we
22215      * don't have to look it up again)
22216      */
22217     cachePosition: function(iPageX, iPageY) {
22218         if (iPageX) {
22219             this.lastPageX = iPageX;
22220             this.lastPageY = iPageY;
22221         } else {
22222             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22223             this.lastPageX = aCoord[0];
22224             this.lastPageY = aCoord[1];
22225         }
22226     },
22227
22228     /**
22229      * Auto-scroll the window if the dragged object has been moved beyond the
22230      * visible window boundary.
22231      * @method autoScroll
22232      * @param {int} x the drag element's x position
22233      * @param {int} y the drag element's y position
22234      * @param {int} h the height of the drag element
22235      * @param {int} w the width of the drag element
22236      * @private
22237      */
22238     autoScroll: function(x, y, h, w) {
22239
22240         if (this.scroll) {
22241             // The client height
22242             var clientH = Roo.lib.Dom.getViewWidth();
22243
22244             // The client width
22245             var clientW = Roo.lib.Dom.getViewHeight();
22246
22247             // The amt scrolled down
22248             var st = this.DDM.getScrollTop();
22249
22250             // The amt scrolled right
22251             var sl = this.DDM.getScrollLeft();
22252
22253             // Location of the bottom of the element
22254             var bot = h + y;
22255
22256             // Location of the right of the element
22257             var right = w + x;
22258
22259             // The distance from the cursor to the bottom of the visible area,
22260             // adjusted so that we don't scroll if the cursor is beyond the
22261             // element drag constraints
22262             var toBot = (clientH + st - y - this.deltaY);
22263
22264             // The distance from the cursor to the right of the visible area
22265             var toRight = (clientW + sl - x - this.deltaX);
22266
22267
22268             // How close to the edge the cursor must be before we scroll
22269             // var thresh = (document.all) ? 100 : 40;
22270             var thresh = 40;
22271
22272             // How many pixels to scroll per autoscroll op.  This helps to reduce
22273             // clunky scrolling. IE is more sensitive about this ... it needs this
22274             // value to be higher.
22275             var scrAmt = (document.all) ? 80 : 30;
22276
22277             // Scroll down if we are near the bottom of the visible page and the
22278             // obj extends below the crease
22279             if ( bot > clientH && toBot < thresh ) {
22280                 window.scrollTo(sl, st + scrAmt);
22281             }
22282
22283             // Scroll up if the window is scrolled down and the top of the object
22284             // goes above the top border
22285             if ( y < st && st > 0 && y - st < thresh ) {
22286                 window.scrollTo(sl, st - scrAmt);
22287             }
22288
22289             // Scroll right if the obj is beyond the right border and the cursor is
22290             // near the border.
22291             if ( right > clientW && toRight < thresh ) {
22292                 window.scrollTo(sl + scrAmt, st);
22293             }
22294
22295             // Scroll left if the window has been scrolled to the right and the obj
22296             // extends past the left border
22297             if ( x < sl && sl > 0 && x - sl < thresh ) {
22298                 window.scrollTo(sl - scrAmt, st);
22299             }
22300         }
22301     },
22302
22303     /**
22304      * Finds the location the element should be placed if we want to move
22305      * it to where the mouse location less the click offset would place us.
22306      * @method getTargetCoord
22307      * @param {int} iPageX the X coordinate of the click
22308      * @param {int} iPageY the Y coordinate of the click
22309      * @return an object that contains the coordinates (Object.x and Object.y)
22310      * @private
22311      */
22312     getTargetCoord: function(iPageX, iPageY) {
22313
22314
22315         var x = iPageX - this.deltaX;
22316         var y = iPageY - this.deltaY;
22317
22318         if (this.constrainX) {
22319             if (x < this.minX) { x = this.minX; }
22320             if (x > this.maxX) { x = this.maxX; }
22321         }
22322
22323         if (this.constrainY) {
22324             if (y < this.minY) { y = this.minY; }
22325             if (y > this.maxY) { y = this.maxY; }
22326         }
22327
22328         x = this.getTick(x, this.xTicks);
22329         y = this.getTick(y, this.yTicks);
22330
22331
22332         return {x:x, y:y};
22333     },
22334
22335     /*
22336      * Sets up config options specific to this class. Overrides
22337      * Roo.dd.DragDrop, but all versions of this method through the
22338      * inheritance chain are called
22339      */
22340     applyConfig: function() {
22341         Roo.dd.DD.superclass.applyConfig.call(this);
22342         this.scroll = (this.config.scroll !== false);
22343     },
22344
22345     /*
22346      * Event that fires prior to the onMouseDown event.  Overrides
22347      * Roo.dd.DragDrop.
22348      */
22349     b4MouseDown: function(e) {
22350         // this.resetConstraints();
22351         this.autoOffset(e.getPageX(),
22352                             e.getPageY());
22353     },
22354
22355     /*
22356      * Event that fires prior to the onDrag event.  Overrides
22357      * Roo.dd.DragDrop.
22358      */
22359     b4Drag: function(e) {
22360         this.setDragElPos(e.getPageX(),
22361                             e.getPageY());
22362     },
22363
22364     toString: function() {
22365         return ("DD " + this.id);
22366     }
22367
22368     //////////////////////////////////////////////////////////////////////////
22369     // Debugging ygDragDrop events that can be overridden
22370     //////////////////////////////////////////////////////////////////////////
22371     /*
22372     startDrag: function(x, y) {
22373     },
22374
22375     onDrag: function(e) {
22376     },
22377
22378     onDragEnter: function(e, id) {
22379     },
22380
22381     onDragOver: function(e, id) {
22382     },
22383
22384     onDragOut: function(e, id) {
22385     },
22386
22387     onDragDrop: function(e, id) {
22388     },
22389
22390     endDrag: function(e) {
22391     }
22392
22393     */
22394
22395 });/*
22396  * Based on:
22397  * Ext JS Library 1.1.1
22398  * Copyright(c) 2006-2007, Ext JS, LLC.
22399  *
22400  * Originally Released Under LGPL - original licence link has changed is not relivant.
22401  *
22402  * Fork - LGPL
22403  * <script type="text/javascript">
22404  */
22405
22406 /**
22407  * @class Roo.dd.DDProxy
22408  * A DragDrop implementation that inserts an empty, bordered div into
22409  * the document that follows the cursor during drag operations.  At the time of
22410  * the click, the frame div is resized to the dimensions of the linked html
22411  * element, and moved to the exact location of the linked element.
22412  *
22413  * References to the "frame" element refer to the single proxy element that
22414  * was created to be dragged in place of all DDProxy elements on the
22415  * page.
22416  *
22417  * @extends Roo.dd.DD
22418  * @constructor
22419  * @param {String} id the id of the linked html element
22420  * @param {String} sGroup the group of related DragDrop objects
22421  * @param {object} config an object containing configurable attributes
22422  *                Valid properties for DDProxy in addition to those in DragDrop:
22423  *                   resizeFrame, centerFrame, dragElId
22424  */
22425 Roo.dd.DDProxy = function(id, sGroup, config) {
22426     if (id) {
22427         this.init(id, sGroup, config);
22428         this.initFrame();
22429     }
22430 };
22431
22432 /**
22433  * The default drag frame div id
22434  * @property Roo.dd.DDProxy.dragElId
22435  * @type String
22436  * @static
22437  */
22438 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22439
22440 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22441
22442     /**
22443      * By default we resize the drag frame to be the same size as the element
22444      * we want to drag (this is to get the frame effect).  We can turn it off
22445      * if we want a different behavior.
22446      * @property resizeFrame
22447      * @type boolean
22448      */
22449     resizeFrame: true,
22450
22451     /**
22452      * By default the frame is positioned exactly where the drag element is, so
22453      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22454      * you do not have constraints on the obj is to have the drag frame centered
22455      * around the cursor.  Set centerFrame to true for this effect.
22456      * @property centerFrame
22457      * @type boolean
22458      */
22459     centerFrame: false,
22460
22461     /**
22462      * Creates the proxy element if it does not yet exist
22463      * @method createFrame
22464      */
22465     createFrame: function() {
22466         var self = this;
22467         var body = document.body;
22468
22469         if (!body || !body.firstChild) {
22470             setTimeout( function() { self.createFrame(); }, 50 );
22471             return;
22472         }
22473
22474         var div = this.getDragEl();
22475
22476         if (!div) {
22477             div    = document.createElement("div");
22478             div.id = this.dragElId;
22479             var s  = div.style;
22480
22481             s.position   = "absolute";
22482             s.visibility = "hidden";
22483             s.cursor     = "move";
22484             s.border     = "2px solid #aaa";
22485             s.zIndex     = 999;
22486
22487             // appendChild can blow up IE if invoked prior to the window load event
22488             // while rendering a table.  It is possible there are other scenarios
22489             // that would cause this to happen as well.
22490             body.insertBefore(div, body.firstChild);
22491         }
22492     },
22493
22494     /**
22495      * Initialization for the drag frame element.  Must be called in the
22496      * constructor of all subclasses
22497      * @method initFrame
22498      */
22499     initFrame: function() {
22500         this.createFrame();
22501     },
22502
22503     applyConfig: function() {
22504         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22505
22506         this.resizeFrame = (this.config.resizeFrame !== false);
22507         this.centerFrame = (this.config.centerFrame);
22508         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22509     },
22510
22511     /**
22512      * Resizes the drag frame to the dimensions of the clicked object, positions
22513      * it over the object, and finally displays it
22514      * @method showFrame
22515      * @param {int} iPageX X click position
22516      * @param {int} iPageY Y click position
22517      * @private
22518      */
22519     showFrame: function(iPageX, iPageY) {
22520         var el = this.getEl();
22521         var dragEl = this.getDragEl();
22522         var s = dragEl.style;
22523
22524         this._resizeProxy();
22525
22526         if (this.centerFrame) {
22527             this.setDelta( Math.round(parseInt(s.width,  10)/2),
22528                            Math.round(parseInt(s.height, 10)/2) );
22529         }
22530
22531         this.setDragElPos(iPageX, iPageY);
22532
22533         Roo.fly(dragEl).show();
22534     },
22535
22536     /**
22537      * The proxy is automatically resized to the dimensions of the linked
22538      * element when a drag is initiated, unless resizeFrame is set to false
22539      * @method _resizeProxy
22540      * @private
22541      */
22542     _resizeProxy: function() {
22543         if (this.resizeFrame) {
22544             var el = this.getEl();
22545             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22546         }
22547     },
22548
22549     // overrides Roo.dd.DragDrop
22550     b4MouseDown: function(e) {
22551         var x = e.getPageX();
22552         var y = e.getPageY();
22553         this.autoOffset(x, y);
22554         this.setDragElPos(x, y);
22555     },
22556
22557     // overrides Roo.dd.DragDrop
22558     b4StartDrag: function(x, y) {
22559         // show the drag frame
22560         this.showFrame(x, y);
22561     },
22562
22563     // overrides Roo.dd.DragDrop
22564     b4EndDrag: function(e) {
22565         Roo.fly(this.getDragEl()).hide();
22566     },
22567
22568     // overrides Roo.dd.DragDrop
22569     // By default we try to move the element to the last location of the frame.
22570     // This is so that the default behavior mirrors that of Roo.dd.DD.
22571     endDrag: function(e) {
22572
22573         var lel = this.getEl();
22574         var del = this.getDragEl();
22575
22576         // Show the drag frame briefly so we can get its position
22577         del.style.visibility = "";
22578
22579         this.beforeMove();
22580         // Hide the linked element before the move to get around a Safari
22581         // rendering bug.
22582         lel.style.visibility = "hidden";
22583         Roo.dd.DDM.moveToEl(lel, del);
22584         del.style.visibility = "hidden";
22585         lel.style.visibility = "";
22586
22587         this.afterDrag();
22588     },
22589
22590     beforeMove : function(){
22591
22592     },
22593
22594     afterDrag : function(){
22595
22596     },
22597
22598     toString: function() {
22599         return ("DDProxy " + this.id);
22600     }
22601
22602 });
22603 /*
22604  * Based on:
22605  * Ext JS Library 1.1.1
22606  * Copyright(c) 2006-2007, Ext JS, LLC.
22607  *
22608  * Originally Released Under LGPL - original licence link has changed is not relivant.
22609  *
22610  * Fork - LGPL
22611  * <script type="text/javascript">
22612  */
22613
22614  /**
22615  * @class Roo.dd.DDTarget
22616  * A DragDrop implementation that does not move, but can be a drop
22617  * target.  You would get the same result by simply omitting implementation
22618  * for the event callbacks, but this way we reduce the processing cost of the
22619  * event listener and the callbacks.
22620  * @extends Roo.dd.DragDrop
22621  * @constructor
22622  * @param {String} id the id of the element that is a drop target
22623  * @param {String} sGroup the group of related DragDrop objects
22624  * @param {object} config an object containing configurable attributes
22625  *                 Valid properties for DDTarget in addition to those in
22626  *                 DragDrop:
22627  *                    none
22628  */
22629 Roo.dd.DDTarget = function(id, sGroup, config) {
22630     if (id) {
22631         this.initTarget(id, sGroup, config);
22632     }
22633     if (config && (config.listeners || config.events)) { 
22634         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
22635             listeners : config.listeners || {}, 
22636             events : config.events || {} 
22637         });    
22638     }
22639 };
22640
22641 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22642 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22643     toString: function() {
22644         return ("DDTarget " + this.id);
22645     }
22646 });
22647 /*
22648  * Based on:
22649  * Ext JS Library 1.1.1
22650  * Copyright(c) 2006-2007, Ext JS, LLC.
22651  *
22652  * Originally Released Under LGPL - original licence link has changed is not relivant.
22653  *
22654  * Fork - LGPL
22655  * <script type="text/javascript">
22656  */
22657  
22658
22659 /**
22660  * @class Roo.dd.ScrollManager
22661  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22662  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22663  * @static
22664  */
22665 Roo.dd.ScrollManager = function(){
22666     var ddm = Roo.dd.DragDropMgr;
22667     var els = {};
22668     var dragEl = null;
22669     var proc = {};
22670     
22671     
22672     
22673     var onStop = function(e){
22674         dragEl = null;
22675         clearProc();
22676     };
22677     
22678     var triggerRefresh = function(){
22679         if(ddm.dragCurrent){
22680              ddm.refreshCache(ddm.dragCurrent.groups);
22681         }
22682     };
22683     
22684     var doScroll = function(){
22685         if(ddm.dragCurrent){
22686             var dds = Roo.dd.ScrollManager;
22687             if(!dds.animate){
22688                 if(proc.el.scroll(proc.dir, dds.increment)){
22689                     triggerRefresh();
22690                 }
22691             }else{
22692                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
22693             }
22694         }
22695     };
22696     
22697     var clearProc = function(){
22698         if(proc.id){
22699             clearInterval(proc.id);
22700         }
22701         proc.id = 0;
22702         proc.el = null;
22703         proc.dir = "";
22704     };
22705     
22706     var startProc = function(el, dir){
22707          Roo.log('scroll startproc');
22708         clearProc();
22709         proc.el = el;
22710         proc.dir = dir;
22711         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
22712     };
22713     
22714     var onFire = function(e, isDrop){
22715        
22716         if(isDrop || !ddm.dragCurrent){ return; }
22717         var dds = Roo.dd.ScrollManager;
22718         if(!dragEl || dragEl != ddm.dragCurrent){
22719             dragEl = ddm.dragCurrent;
22720             // refresh regions on drag start
22721             dds.refreshCache();
22722         }
22723         
22724         var xy = Roo.lib.Event.getXY(e);
22725         var pt = new Roo.lib.Point(xy[0], xy[1]);
22726         for(var id in els){
22727             var el = els[id], r = el._region;
22728             if(r && r.contains(pt) && el.isScrollable()){
22729                 if(r.bottom - pt.y <= dds.thresh){
22730                     if(proc.el != el){
22731                         startProc(el, "down");
22732                     }
22733                     return;
22734                 }else if(r.right - pt.x <= dds.thresh){
22735                     if(proc.el != el){
22736                         startProc(el, "left");
22737                     }
22738                     return;
22739                 }else if(pt.y - r.top <= dds.thresh){
22740                     if(proc.el != el){
22741                         startProc(el, "up");
22742                     }
22743                     return;
22744                 }else if(pt.x - r.left <= dds.thresh){
22745                     if(proc.el != el){
22746                         startProc(el, "right");
22747                     }
22748                     return;
22749                 }
22750             }
22751         }
22752         clearProc();
22753     };
22754     
22755     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
22756     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
22757     
22758     return {
22759         /**
22760          * Registers new overflow element(s) to auto scroll
22761          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
22762          */
22763         register : function(el){
22764             if(el instanceof Array){
22765                 for(var i = 0, len = el.length; i < len; i++) {
22766                         this.register(el[i]);
22767                 }
22768             }else{
22769                 el = Roo.get(el);
22770                 els[el.id] = el;
22771             }
22772             Roo.dd.ScrollManager.els = els;
22773         },
22774         
22775         /**
22776          * Unregisters overflow element(s) so they are no longer scrolled
22777          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
22778          */
22779         unregister : function(el){
22780             if(el instanceof Array){
22781                 for(var i = 0, len = el.length; i < len; i++) {
22782                         this.unregister(el[i]);
22783                 }
22784             }else{
22785                 el = Roo.get(el);
22786                 delete els[el.id];
22787             }
22788         },
22789         
22790         /**
22791          * The number of pixels from the edge of a container the pointer needs to be to 
22792          * trigger scrolling (defaults to 25)
22793          * @type Number
22794          */
22795         thresh : 25,
22796         
22797         /**
22798          * The number of pixels to scroll in each scroll increment (defaults to 50)
22799          * @type Number
22800          */
22801         increment : 100,
22802         
22803         /**
22804          * The frequency of scrolls in milliseconds (defaults to 500)
22805          * @type Number
22806          */
22807         frequency : 500,
22808         
22809         /**
22810          * True to animate the scroll (defaults to true)
22811          * @type Boolean
22812          */
22813         animate: true,
22814         
22815         /**
22816          * The animation duration in seconds - 
22817          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
22818          * @type Number
22819          */
22820         animDuration: .4,
22821         
22822         /**
22823          * Manually trigger a cache refresh.
22824          */
22825         refreshCache : function(){
22826             for(var id in els){
22827                 if(typeof els[id] == 'object'){ // for people extending the object prototype
22828                     els[id]._region = els[id].getRegion();
22829                 }
22830             }
22831         }
22832     };
22833 }();/*
22834  * Based on:
22835  * Ext JS Library 1.1.1
22836  * Copyright(c) 2006-2007, Ext JS, LLC.
22837  *
22838  * Originally Released Under LGPL - original licence link has changed is not relivant.
22839  *
22840  * Fork - LGPL
22841  * <script type="text/javascript">
22842  */
22843  
22844
22845 /**
22846  * @class Roo.dd.Registry
22847  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
22848  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
22849  * @static
22850  */
22851 Roo.dd.Registry = function(){
22852     var elements = {}; 
22853     var handles = {}; 
22854     var autoIdSeed = 0;
22855
22856     var getId = function(el, autogen){
22857         if(typeof el == "string"){
22858             return el;
22859         }
22860         var id = el.id;
22861         if(!id && autogen !== false){
22862             id = "roodd-" + (++autoIdSeed);
22863             el.id = id;
22864         }
22865         return id;
22866     };
22867     
22868     return {
22869     /**
22870      * Register a drag drop element
22871      * @param {String|HTMLElement} element The id or DOM node to register
22872      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
22873      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
22874      * knows how to interpret, plus there are some specific properties known to the Registry that should be
22875      * populated in the data object (if applicable):
22876      * <pre>
22877 Value      Description<br />
22878 ---------  ------------------------------------------<br />
22879 handles    Array of DOM nodes that trigger dragging<br />
22880            for the element being registered<br />
22881 isHandle   True if the element passed in triggers<br />
22882            dragging itself, else false
22883 </pre>
22884      */
22885         register : function(el, data){
22886             data = data || {};
22887             if(typeof el == "string"){
22888                 el = document.getElementById(el);
22889             }
22890             data.ddel = el;
22891             elements[getId(el)] = data;
22892             if(data.isHandle !== false){
22893                 handles[data.ddel.id] = data;
22894             }
22895             if(data.handles){
22896                 var hs = data.handles;
22897                 for(var i = 0, len = hs.length; i < len; i++){
22898                         handles[getId(hs[i])] = data;
22899                 }
22900             }
22901         },
22902
22903     /**
22904      * Unregister a drag drop element
22905      * @param {String|HTMLElement}  element The id or DOM node to unregister
22906      */
22907         unregister : function(el){
22908             var id = getId(el, false);
22909             var data = elements[id];
22910             if(data){
22911                 delete elements[id];
22912                 if(data.handles){
22913                     var hs = data.handles;
22914                     for(var i = 0, len = hs.length; i < len; i++){
22915                         delete handles[getId(hs[i], false)];
22916                     }
22917                 }
22918             }
22919         },
22920
22921     /**
22922      * Returns the handle registered for a DOM Node by id
22923      * @param {String|HTMLElement} id The DOM node or id to look up
22924      * @return {Object} handle The custom handle data
22925      */
22926         getHandle : function(id){
22927             if(typeof id != "string"){ // must be element?
22928                 id = id.id;
22929             }
22930             return handles[id];
22931         },
22932
22933     /**
22934      * Returns the handle that is registered for the DOM node that is the target of the event
22935      * @param {Event} e The event
22936      * @return {Object} handle The custom handle data
22937      */
22938         getHandleFromEvent : function(e){
22939             var t = Roo.lib.Event.getTarget(e);
22940             return t ? handles[t.id] : null;
22941         },
22942
22943     /**
22944      * Returns a custom data object that is registered for a DOM node by id
22945      * @param {String|HTMLElement} id The DOM node or id to look up
22946      * @return {Object} data The custom data
22947      */
22948         getTarget : function(id){
22949             if(typeof id != "string"){ // must be element?
22950                 id = id.id;
22951             }
22952             return elements[id];
22953         },
22954
22955     /**
22956      * Returns a custom data object that is registered for the DOM node that is the target of the event
22957      * @param {Event} e The event
22958      * @return {Object} data The custom data
22959      */
22960         getTargetFromEvent : function(e){
22961             var t = Roo.lib.Event.getTarget(e);
22962             return t ? elements[t.id] || handles[t.id] : null;
22963         }
22964     };
22965 }();/*
22966  * Based on:
22967  * Ext JS Library 1.1.1
22968  * Copyright(c) 2006-2007, Ext JS, LLC.
22969  *
22970  * Originally Released Under LGPL - original licence link has changed is not relivant.
22971  *
22972  * Fork - LGPL
22973  * <script type="text/javascript">
22974  */
22975  
22976
22977 /**
22978  * @class Roo.dd.StatusProxy
22979  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22980  * default drag proxy used by all Roo.dd components.
22981  * @constructor
22982  * @param {Object} config
22983  */
22984 Roo.dd.StatusProxy = function(config){
22985     Roo.apply(this, config);
22986     this.id = this.id || Roo.id();
22987     this.el = new Roo.Layer({
22988         dh: {
22989             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22990                 {tag: "div", cls: "x-dd-drop-icon"},
22991                 {tag: "div", cls: "x-dd-drag-ghost"}
22992             ]
22993         }, 
22994         shadow: !config || config.shadow !== false
22995     });
22996     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22997     this.dropStatus = this.dropNotAllowed;
22998 };
22999
23000 Roo.dd.StatusProxy.prototype = {
23001     /**
23002      * @cfg {String} dropAllowed
23003      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23004      */
23005     dropAllowed : "x-dd-drop-ok",
23006     /**
23007      * @cfg {String} dropNotAllowed
23008      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23009      */
23010     dropNotAllowed : "x-dd-drop-nodrop",
23011
23012     /**
23013      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23014      * over the current target element.
23015      * @param {String} cssClass The css class for the new drop status indicator image
23016      */
23017     setStatus : function(cssClass){
23018         cssClass = cssClass || this.dropNotAllowed;
23019         if(this.dropStatus != cssClass){
23020             this.el.replaceClass(this.dropStatus, cssClass);
23021             this.dropStatus = cssClass;
23022         }
23023     },
23024
23025     /**
23026      * Resets the status indicator to the default dropNotAllowed value
23027      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23028      */
23029     reset : function(clearGhost){
23030         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23031         this.dropStatus = this.dropNotAllowed;
23032         if(clearGhost){
23033             this.ghost.update("");
23034         }
23035     },
23036
23037     /**
23038      * Updates the contents of the ghost element
23039      * @param {String} html The html that will replace the current innerHTML of the ghost element
23040      */
23041     update : function(html){
23042         if(typeof html == "string"){
23043             this.ghost.update(html);
23044         }else{
23045             this.ghost.update("");
23046             html.style.margin = "0";
23047             this.ghost.dom.appendChild(html);
23048         }
23049         // ensure float = none set?? cant remember why though.
23050         var el = this.ghost.dom.firstChild;
23051                 if(el){
23052                         Roo.fly(el).setStyle('float', 'none');
23053                 }
23054     },
23055     
23056     /**
23057      * Returns the underlying proxy {@link Roo.Layer}
23058      * @return {Roo.Layer} el
23059     */
23060     getEl : function(){
23061         return this.el;
23062     },
23063
23064     /**
23065      * Returns the ghost element
23066      * @return {Roo.Element} el
23067      */
23068     getGhost : function(){
23069         return this.ghost;
23070     },
23071
23072     /**
23073      * Hides the proxy
23074      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23075      */
23076     hide : function(clear){
23077         this.el.hide();
23078         if(clear){
23079             this.reset(true);
23080         }
23081     },
23082
23083     /**
23084      * Stops the repair animation if it's currently running
23085      */
23086     stop : function(){
23087         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23088             this.anim.stop();
23089         }
23090     },
23091
23092     /**
23093      * Displays this proxy
23094      */
23095     show : function(){
23096         this.el.show();
23097     },
23098
23099     /**
23100      * Force the Layer to sync its shadow and shim positions to the element
23101      */
23102     sync : function(){
23103         this.el.sync();
23104     },
23105
23106     /**
23107      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23108      * invalid drop operation by the item being dragged.
23109      * @param {Array} xy The XY position of the element ([x, y])
23110      * @param {Function} callback The function to call after the repair is complete
23111      * @param {Object} scope The scope in which to execute the callback
23112      */
23113     repair : function(xy, callback, scope){
23114         this.callback = callback;
23115         this.scope = scope;
23116         if(xy && this.animRepair !== false){
23117             this.el.addClass("x-dd-drag-repair");
23118             this.el.hideUnders(true);
23119             this.anim = this.el.shift({
23120                 duration: this.repairDuration || .5,
23121                 easing: 'easeOut',
23122                 xy: xy,
23123                 stopFx: true,
23124                 callback: this.afterRepair,
23125                 scope: this
23126             });
23127         }else{
23128             this.afterRepair();
23129         }
23130     },
23131
23132     // private
23133     afterRepair : function(){
23134         this.hide(true);
23135         if(typeof this.callback == "function"){
23136             this.callback.call(this.scope || this);
23137         }
23138         this.callback = null;
23139         this.scope = null;
23140     }
23141 };/*
23142  * Based on:
23143  * Ext JS Library 1.1.1
23144  * Copyright(c) 2006-2007, Ext JS, LLC.
23145  *
23146  * Originally Released Under LGPL - original licence link has changed is not relivant.
23147  *
23148  * Fork - LGPL
23149  * <script type="text/javascript">
23150  */
23151
23152 /**
23153  * @class Roo.dd.DragSource
23154  * @extends Roo.dd.DDProxy
23155  * A simple class that provides the basic implementation needed to make any element draggable.
23156  * @constructor
23157  * @param {String/HTMLElement/Element} el The container element
23158  * @param {Object} config
23159  */
23160 Roo.dd.DragSource = function(el, config){
23161     this.el = Roo.get(el);
23162     this.dragData = {};
23163     
23164     Roo.apply(this, config);
23165     
23166     if(!this.proxy){
23167         this.proxy = new Roo.dd.StatusProxy();
23168     }
23169
23170     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23171           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23172     
23173     this.dragging = false;
23174 };
23175
23176 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23177     /**
23178      * @cfg {String} dropAllowed
23179      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23180      */
23181     dropAllowed : "x-dd-drop-ok",
23182     /**
23183      * @cfg {String} dropNotAllowed
23184      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23185      */
23186     dropNotAllowed : "x-dd-drop-nodrop",
23187
23188     /**
23189      * Returns the data object associated with this drag source
23190      * @return {Object} data An object containing arbitrary data
23191      */
23192     getDragData : function(e){
23193         return this.dragData;
23194     },
23195
23196     // private
23197     onDragEnter : function(e, id){
23198         var target = Roo.dd.DragDropMgr.getDDById(id);
23199         this.cachedTarget = target;
23200         if(this.beforeDragEnter(target, e, id) !== false){
23201             if(target.isNotifyTarget){
23202                 var status = target.notifyEnter(this, e, this.dragData);
23203                 this.proxy.setStatus(status);
23204             }else{
23205                 this.proxy.setStatus(this.dropAllowed);
23206             }
23207             
23208             if(this.afterDragEnter){
23209                 /**
23210                  * An empty function by default, but provided so that you can perform a custom action
23211                  * when the dragged item enters the drop target by providing an implementation.
23212                  * @param {Roo.dd.DragDrop} target The drop target
23213                  * @param {Event} e The event object
23214                  * @param {String} id The id of the dragged element
23215                  * @method afterDragEnter
23216                  */
23217                 this.afterDragEnter(target, e, id);
23218             }
23219         }
23220     },
23221
23222     /**
23223      * An empty function by default, but provided so that you can perform a custom action
23224      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23225      * @param {Roo.dd.DragDrop} target The drop target
23226      * @param {Event} e The event object
23227      * @param {String} id The id of the dragged element
23228      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23229      */
23230     beforeDragEnter : function(target, e, id){
23231         return true;
23232     },
23233
23234     // private
23235     alignElWithMouse: function() {
23236         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23237         this.proxy.sync();
23238     },
23239
23240     // private
23241     onDragOver : function(e, id){
23242         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23243         if(this.beforeDragOver(target, e, id) !== false){
23244             if(target.isNotifyTarget){
23245                 var status = target.notifyOver(this, e, this.dragData);
23246                 this.proxy.setStatus(status);
23247             }
23248
23249             if(this.afterDragOver){
23250                 /**
23251                  * An empty function by default, but provided so that you can perform a custom action
23252                  * while the dragged item is over the drop target by providing an implementation.
23253                  * @param {Roo.dd.DragDrop} target The drop target
23254                  * @param {Event} e The event object
23255                  * @param {String} id The id of the dragged element
23256                  * @method afterDragOver
23257                  */
23258                 this.afterDragOver(target, e, id);
23259             }
23260         }
23261     },
23262
23263     /**
23264      * An empty function by default, but provided so that you can perform a custom action
23265      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23266      * @param {Roo.dd.DragDrop} target The drop target
23267      * @param {Event} e The event object
23268      * @param {String} id The id of the dragged element
23269      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23270      */
23271     beforeDragOver : function(target, e, id){
23272         return true;
23273     },
23274
23275     // private
23276     onDragOut : function(e, id){
23277         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23278         if(this.beforeDragOut(target, e, id) !== false){
23279             if(target.isNotifyTarget){
23280                 target.notifyOut(this, e, this.dragData);
23281             }
23282             this.proxy.reset();
23283             if(this.afterDragOut){
23284                 /**
23285                  * An empty function by default, but provided so that you can perform a custom action
23286                  * after the dragged item is dragged out of the target without dropping.
23287                  * @param {Roo.dd.DragDrop} target The drop target
23288                  * @param {Event} e The event object
23289                  * @param {String} id The id of the dragged element
23290                  * @method afterDragOut
23291                  */
23292                 this.afterDragOut(target, e, id);
23293             }
23294         }
23295         this.cachedTarget = null;
23296     },
23297
23298     /**
23299      * An empty function by default, but provided so that you can perform a custom action before the dragged
23300      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23301      * @param {Roo.dd.DragDrop} target The drop target
23302      * @param {Event} e The event object
23303      * @param {String} id The id of the dragged element
23304      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23305      */
23306     beforeDragOut : function(target, e, id){
23307         return true;
23308     },
23309     
23310     // private
23311     onDragDrop : function(e, id){
23312         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23313         if(this.beforeDragDrop(target, e, id) !== false){
23314             if(target.isNotifyTarget){
23315                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23316                     this.onValidDrop(target, e, id);
23317                 }else{
23318                     this.onInvalidDrop(target, e, id);
23319                 }
23320             }else{
23321                 this.onValidDrop(target, e, id);
23322             }
23323             
23324             if(this.afterDragDrop){
23325                 /**
23326                  * An empty function by default, but provided so that you can perform a custom action
23327                  * after a valid drag drop has occurred by providing an implementation.
23328                  * @param {Roo.dd.DragDrop} target The drop target
23329                  * @param {Event} e The event object
23330                  * @param {String} id The id of the dropped element
23331                  * @method afterDragDrop
23332                  */
23333                 this.afterDragDrop(target, e, id);
23334             }
23335         }
23336         delete this.cachedTarget;
23337     },
23338
23339     /**
23340      * An empty function by default, but provided so that you can perform a custom action before the dragged
23341      * item is dropped onto the target and optionally cancel the onDragDrop.
23342      * @param {Roo.dd.DragDrop} target The drop target
23343      * @param {Event} e The event object
23344      * @param {String} id The id of the dragged element
23345      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23346      */
23347     beforeDragDrop : function(target, e, id){
23348         return true;
23349     },
23350
23351     // private
23352     onValidDrop : function(target, e, id){
23353         this.hideProxy();
23354         if(this.afterValidDrop){
23355             /**
23356              * An empty function by default, but provided so that you can perform a custom action
23357              * after a valid drop has occurred by providing an implementation.
23358              * @param {Object} target The target DD 
23359              * @param {Event} e The event object
23360              * @param {String} id The id of the dropped element
23361              * @method afterInvalidDrop
23362              */
23363             this.afterValidDrop(target, e, id);
23364         }
23365     },
23366
23367     // private
23368     getRepairXY : function(e, data){
23369         return this.el.getXY();  
23370     },
23371
23372     // private
23373     onInvalidDrop : function(target, e, id){
23374         this.beforeInvalidDrop(target, e, id);
23375         if(this.cachedTarget){
23376             if(this.cachedTarget.isNotifyTarget){
23377                 this.cachedTarget.notifyOut(this, e, this.dragData);
23378             }
23379             this.cacheTarget = null;
23380         }
23381         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23382
23383         if(this.afterInvalidDrop){
23384             /**
23385              * An empty function by default, but provided so that you can perform a custom action
23386              * after an invalid drop has occurred by providing an implementation.
23387              * @param {Event} e The event object
23388              * @param {String} id The id of the dropped element
23389              * @method afterInvalidDrop
23390              */
23391             this.afterInvalidDrop(e, id);
23392         }
23393     },
23394
23395     // private
23396     afterRepair : function(){
23397         if(Roo.enableFx){
23398             this.el.highlight(this.hlColor || "c3daf9");
23399         }
23400         this.dragging = false;
23401     },
23402
23403     /**
23404      * An empty function by default, but provided so that you can perform a custom action after an invalid
23405      * drop has occurred.
23406      * @param {Roo.dd.DragDrop} target The drop target
23407      * @param {Event} e The event object
23408      * @param {String} id The id of the dragged element
23409      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23410      */
23411     beforeInvalidDrop : function(target, e, id){
23412         return true;
23413     },
23414
23415     // private
23416     handleMouseDown : function(e){
23417         if(this.dragging) {
23418             return;
23419         }
23420         var data = this.getDragData(e);
23421         if(data && this.onBeforeDrag(data, e) !== false){
23422             this.dragData = data;
23423             this.proxy.stop();
23424             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23425         } 
23426     },
23427
23428     /**
23429      * An empty function by default, but provided so that you can perform a custom action before the initial
23430      * drag event begins and optionally cancel it.
23431      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23432      * @param {Event} e The event object
23433      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23434      */
23435     onBeforeDrag : function(data, e){
23436         return true;
23437     },
23438
23439     /**
23440      * An empty function by default, but provided so that you can perform a custom action once the initial
23441      * drag event has begun.  The drag cannot be canceled from this function.
23442      * @param {Number} x The x position of the click on the dragged object
23443      * @param {Number} y The y position of the click on the dragged object
23444      */
23445     onStartDrag : Roo.emptyFn,
23446
23447     // private - YUI override
23448     startDrag : function(x, y){
23449         this.proxy.reset();
23450         this.dragging = true;
23451         this.proxy.update("");
23452         this.onInitDrag(x, y);
23453         this.proxy.show();
23454     },
23455
23456     // private
23457     onInitDrag : function(x, y){
23458         var clone = this.el.dom.cloneNode(true);
23459         clone.id = Roo.id(); // prevent duplicate ids
23460         this.proxy.update(clone);
23461         this.onStartDrag(x, y);
23462         return true;
23463     },
23464
23465     /**
23466      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23467      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23468      */
23469     getProxy : function(){
23470         return this.proxy;  
23471     },
23472
23473     /**
23474      * Hides the drag source's {@link Roo.dd.StatusProxy}
23475      */
23476     hideProxy : function(){
23477         this.proxy.hide();  
23478         this.proxy.reset(true);
23479         this.dragging = false;
23480     },
23481
23482     // private
23483     triggerCacheRefresh : function(){
23484         Roo.dd.DDM.refreshCache(this.groups);
23485     },
23486
23487     // private - override to prevent hiding
23488     b4EndDrag: function(e) {
23489     },
23490
23491     // private - override to prevent moving
23492     endDrag : function(e){
23493         this.onEndDrag(this.dragData, e);
23494     },
23495
23496     // private
23497     onEndDrag : function(data, e){
23498     },
23499     
23500     // private - pin to cursor
23501     autoOffset : function(x, y) {
23502         this.setDelta(-12, -20);
23503     }    
23504 });/*
23505  * Based on:
23506  * Ext JS Library 1.1.1
23507  * Copyright(c) 2006-2007, Ext JS, LLC.
23508  *
23509  * Originally Released Under LGPL - original licence link has changed is not relivant.
23510  *
23511  * Fork - LGPL
23512  * <script type="text/javascript">
23513  */
23514
23515
23516 /**
23517  * @class Roo.dd.DropTarget
23518  * @extends Roo.dd.DDTarget
23519  * A simple class that provides the basic implementation needed to make any element a drop target that can have
23520  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
23521  * @constructor
23522  * @param {String/HTMLElement/Element} el The container element
23523  * @param {Object} config
23524  */
23525 Roo.dd.DropTarget = function(el, config){
23526     this.el = Roo.get(el);
23527     
23528     var listeners = false; ;
23529     if (config && config.listeners) {
23530         listeners= config.listeners;
23531         delete config.listeners;
23532     }
23533     Roo.apply(this, config);
23534     
23535     if(this.containerScroll){
23536         Roo.dd.ScrollManager.register(this.el);
23537     }
23538     this.addEvents( {
23539          /**
23540          * @scope Roo.dd.DropTarget
23541          */
23542          
23543          /**
23544          * @event enter
23545          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23546          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
23547          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
23548          * 
23549          * IMPORTANT : it should set  this.valid to true|false
23550          * 
23551          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23552          * @param {Event} e The event
23553          * @param {Object} data An object containing arbitrary data supplied by the drag source
23554          */
23555         "enter" : true,
23556         
23557          /**
23558          * @event over
23559          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23560          * This method will be called on every mouse movement while the drag source is over the drop target.
23561          * This default implementation simply returns the dropAllowed config value.
23562          * 
23563          * IMPORTANT : it should set  this.valid to true|false
23564          * 
23565          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23566          * @param {Event} e The event
23567          * @param {Object} data An object containing arbitrary data supplied by the drag source
23568          
23569          */
23570         "over" : true,
23571         /**
23572          * @event out
23573          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23574          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
23575          * overClass (if any) from the drop element.
23576          * 
23577          * 
23578          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23579          * @param {Event} e The event
23580          * @param {Object} data An object containing arbitrary data supplied by the drag source
23581          */
23582          "out" : true,
23583          
23584         /**
23585          * @event drop
23586          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23587          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
23588          * implementation that does something to process the drop event and returns true so that the drag source's
23589          * repair action does not run.
23590          * 
23591          * IMPORTANT : it should set this.success
23592          * 
23593          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23594          * @param {Event} e The event
23595          * @param {Object} data An object containing arbitrary data supplied by the drag source
23596         */
23597          "drop" : true
23598     });
23599             
23600      
23601     Roo.dd.DropTarget.superclass.constructor.call(  this, 
23602         this.el.dom, 
23603         this.ddGroup || this.group,
23604         {
23605             isTarget: true,
23606             listeners : listeners || {} 
23607            
23608         
23609         }
23610     );
23611
23612 };
23613
23614 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23615     /**
23616      * @cfg {String} overClass
23617      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23618      */
23619      /**
23620      * @cfg {String} ddGroup
23621      * The drag drop group to handle drop events for
23622      */
23623      
23624     /**
23625      * @cfg {String} dropAllowed
23626      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23627      */
23628     dropAllowed : "x-dd-drop-ok",
23629     /**
23630      * @cfg {String} dropNotAllowed
23631      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23632      */
23633     dropNotAllowed : "x-dd-drop-nodrop",
23634     /**
23635      * @cfg {boolean} success
23636      * set this after drop listener.. 
23637      */
23638     success : false,
23639     /**
23640      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23641      * if the drop point is valid for over/enter..
23642      */
23643     valid : false,
23644     // private
23645     isTarget : true,
23646
23647     // private
23648     isNotifyTarget : true,
23649     
23650     /**
23651      * @hide
23652      */
23653     notifyEnter : function(dd, e, data)
23654     {
23655         this.valid = true;
23656         this.fireEvent('enter', dd, e, data);
23657         if(this.overClass){
23658             this.el.addClass(this.overClass);
23659         }
23660         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23661             this.valid ? this.dropAllowed : this.dropNotAllowed
23662         );
23663     },
23664
23665     /**
23666      * @hide
23667      */
23668     notifyOver : function(dd, e, data)
23669     {
23670         this.valid = true;
23671         this.fireEvent('over', dd, e, data);
23672         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23673             this.valid ? this.dropAllowed : this.dropNotAllowed
23674         );
23675     },
23676
23677     /**
23678      * @hide
23679      */
23680     notifyOut : function(dd, e, data)
23681     {
23682         this.fireEvent('out', dd, e, data);
23683         if(this.overClass){
23684             this.el.removeClass(this.overClass);
23685         }
23686     },
23687
23688     /**
23689      * @hide
23690      */
23691     notifyDrop : function(dd, e, data)
23692     {
23693         this.success = false;
23694         this.fireEvent('drop', dd, e, data);
23695         return this.success;
23696     }
23697 });/*
23698  * Based on:
23699  * Ext JS Library 1.1.1
23700  * Copyright(c) 2006-2007, Ext JS, LLC.
23701  *
23702  * Originally Released Under LGPL - original licence link has changed is not relivant.
23703  *
23704  * Fork - LGPL
23705  * <script type="text/javascript">
23706  */
23707
23708
23709 /**
23710  * @class Roo.dd.DragZone
23711  * @extends Roo.dd.DragSource
23712  * This class provides a container DD instance that proxies for multiple child node sources.<br />
23713  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
23714  * @constructor
23715  * @param {String/HTMLElement/Element} el The container element
23716  * @param {Object} config
23717  */
23718 Roo.dd.DragZone = function(el, config){
23719     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
23720     if(this.containerScroll){
23721         Roo.dd.ScrollManager.register(this.el);
23722     }
23723 };
23724
23725 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
23726     /**
23727      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
23728      * for auto scrolling during drag operations.
23729      */
23730     /**
23731      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
23732      * method after a failed drop (defaults to "c3daf9" - light blue)
23733      */
23734
23735     /**
23736      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
23737      * for a valid target to drag based on the mouse down. Override this method
23738      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
23739      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
23740      * @param {EventObject} e The mouse down event
23741      * @return {Object} The dragData
23742      */
23743     getDragData : function(e){
23744         return Roo.dd.Registry.getHandleFromEvent(e);
23745     },
23746     
23747     /**
23748      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
23749      * this.dragData.ddel
23750      * @param {Number} x The x position of the click on the dragged object
23751      * @param {Number} y The y position of the click on the dragged object
23752      * @return {Boolean} true to continue the drag, false to cancel
23753      */
23754     onInitDrag : function(x, y){
23755         this.proxy.update(this.dragData.ddel.cloneNode(true));
23756         this.onStartDrag(x, y);
23757         return true;
23758     },
23759     
23760     /**
23761      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
23762      */
23763     afterRepair : function(){
23764         if(Roo.enableFx){
23765             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
23766         }
23767         this.dragging = false;
23768     },
23769
23770     /**
23771      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
23772      * the XY of this.dragData.ddel
23773      * @param {EventObject} e The mouse up event
23774      * @return {Array} The xy location (e.g. [100, 200])
23775      */
23776     getRepairXY : function(e){
23777         return Roo.Element.fly(this.dragData.ddel).getXY();  
23778     }
23779 });/*
23780  * Based on:
23781  * Ext JS Library 1.1.1
23782  * Copyright(c) 2006-2007, Ext JS, LLC.
23783  *
23784  * Originally Released Under LGPL - original licence link has changed is not relivant.
23785  *
23786  * Fork - LGPL
23787  * <script type="text/javascript">
23788  */
23789 /**
23790  * @class Roo.dd.DropZone
23791  * @extends Roo.dd.DropTarget
23792  * This class provides a container DD instance that proxies for multiple child node targets.<br />
23793  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
23794  * @constructor
23795  * @param {String/HTMLElement/Element} el The container element
23796  * @param {Object} config
23797  */
23798 Roo.dd.DropZone = function(el, config){
23799     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
23800 };
23801
23802 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
23803     /**
23804      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
23805      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
23806      * provide your own custom lookup.
23807      * @param {Event} e The event
23808      * @return {Object} data The custom data
23809      */
23810     getTargetFromEvent : function(e){
23811         return Roo.dd.Registry.getTargetFromEvent(e);
23812     },
23813
23814     /**
23815      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
23816      * that it has registered.  This method has no default implementation and should be overridden to provide
23817      * node-specific processing if necessary.
23818      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
23819      * {@link #getTargetFromEvent} for this node)
23820      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23821      * @param {Event} e The event
23822      * @param {Object} data An object containing arbitrary data supplied by the drag source
23823      */
23824     onNodeEnter : function(n, dd, e, data){
23825         
23826     },
23827
23828     /**
23829      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
23830      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
23831      * overridden to provide the proper feedback.
23832      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23833      * {@link #getTargetFromEvent} for this node)
23834      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23835      * @param {Event} e The event
23836      * @param {Object} data An object containing arbitrary data supplied by the drag source
23837      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23838      * underlying {@link Roo.dd.StatusProxy} can be updated
23839      */
23840     onNodeOver : function(n, dd, e, data){
23841         return this.dropAllowed;
23842     },
23843
23844     /**
23845      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
23846      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
23847      * node-specific processing if necessary.
23848      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23849      * {@link #getTargetFromEvent} for this node)
23850      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23851      * @param {Event} e The event
23852      * @param {Object} data An object containing arbitrary data supplied by the drag source
23853      */
23854     onNodeOut : function(n, dd, e, data){
23855         
23856     },
23857
23858     /**
23859      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
23860      * the drop node.  The default implementation returns false, so it should be overridden to provide the
23861      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
23862      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23863      * {@link #getTargetFromEvent} for this node)
23864      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23865      * @param {Event} e The event
23866      * @param {Object} data An object containing arbitrary data supplied by the drag source
23867      * @return {Boolean} True if the drop was valid, else false
23868      */
23869     onNodeDrop : function(n, dd, e, data){
23870         return false;
23871     },
23872
23873     /**
23874      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
23875      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
23876      * it should be overridden to provide the proper feedback if necessary.
23877      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23878      * @param {Event} e The event
23879      * @param {Object} data An object containing arbitrary data supplied by the drag source
23880      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23881      * underlying {@link Roo.dd.StatusProxy} can be updated
23882      */
23883     onContainerOver : function(dd, e, data){
23884         return this.dropNotAllowed;
23885     },
23886
23887     /**
23888      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23889      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23890      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23891      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23892      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23893      * @param {Event} e The event
23894      * @param {Object} data An object containing arbitrary data supplied by the drag source
23895      * @return {Boolean} True if the drop was valid, else false
23896      */
23897     onContainerDrop : function(dd, e, data){
23898         return false;
23899     },
23900
23901     /**
23902      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23903      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23904      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23905      * you should override this method and provide a custom implementation.
23906      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23907      * @param {Event} e The event
23908      * @param {Object} data An object containing arbitrary data supplied by the drag source
23909      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23910      * underlying {@link Roo.dd.StatusProxy} can be updated
23911      */
23912     notifyEnter : function(dd, e, data){
23913         return this.dropNotAllowed;
23914     },
23915
23916     /**
23917      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23918      * This method will be called on every mouse movement while the drag source is over the drop zone.
23919      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23920      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23921      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23922      * registered node, it will call {@link #onContainerOver}.
23923      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23924      * @param {Event} e The event
23925      * @param {Object} data An object containing arbitrary data supplied by the drag source
23926      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23927      * underlying {@link Roo.dd.StatusProxy} can be updated
23928      */
23929     notifyOver : function(dd, e, data){
23930         var n = this.getTargetFromEvent(e);
23931         if(!n){ // not over valid drop target
23932             if(this.lastOverNode){
23933                 this.onNodeOut(this.lastOverNode, dd, e, data);
23934                 this.lastOverNode = null;
23935             }
23936             return this.onContainerOver(dd, e, data);
23937         }
23938         if(this.lastOverNode != n){
23939             if(this.lastOverNode){
23940                 this.onNodeOut(this.lastOverNode, dd, e, data);
23941             }
23942             this.onNodeEnter(n, dd, e, data);
23943             this.lastOverNode = n;
23944         }
23945         return this.onNodeOver(n, dd, e, data);
23946     },
23947
23948     /**
23949      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23950      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23951      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23952      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23953      * @param {Event} e The event
23954      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23955      */
23956     notifyOut : function(dd, e, data){
23957         if(this.lastOverNode){
23958             this.onNodeOut(this.lastOverNode, dd, e, data);
23959             this.lastOverNode = null;
23960         }
23961     },
23962
23963     /**
23964      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23965      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23966      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23967      * otherwise it will call {@link #onContainerDrop}.
23968      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23969      * @param {Event} e The event
23970      * @param {Object} data An object containing arbitrary data supplied by the drag source
23971      * @return {Boolean} True if the drop was valid, else false
23972      */
23973     notifyDrop : function(dd, e, data){
23974         if(this.lastOverNode){
23975             this.onNodeOut(this.lastOverNode, dd, e, data);
23976             this.lastOverNode = null;
23977         }
23978         var n = this.getTargetFromEvent(e);
23979         return n ?
23980             this.onNodeDrop(n, dd, e, data) :
23981             this.onContainerDrop(dd, e, data);
23982     },
23983
23984     // private
23985     triggerCacheRefresh : function(){
23986         Roo.dd.DDM.refreshCache(this.groups);
23987     }  
23988 });/*
23989  * Based on:
23990  * Ext JS Library 1.1.1
23991  * Copyright(c) 2006-2007, Ext JS, LLC.
23992  *
23993  * Originally Released Under LGPL - original licence link has changed is not relivant.
23994  *
23995  * Fork - LGPL
23996  * <script type="text/javascript">
23997  */
23998
23999
24000 /**
24001  * @class Roo.data.SortTypes
24002  * @static
24003  * Defines the default sorting (casting?) comparison functions used when sorting data.
24004  */
24005 Roo.data.SortTypes = {
24006     /**
24007      * Default sort that does nothing
24008      * @param {Mixed} s The value being converted
24009      * @return {Mixed} The comparison value
24010      */
24011     none : function(s){
24012         return s;
24013     },
24014     
24015     /**
24016      * The regular expression used to strip tags
24017      * @type {RegExp}
24018      * @property
24019      */
24020     stripTagsRE : /<\/?[^>]+>/gi,
24021     
24022     /**
24023      * Strips all HTML tags to sort on text only
24024      * @param {Mixed} s The value being converted
24025      * @return {String} The comparison value
24026      */
24027     asText : function(s){
24028         return String(s).replace(this.stripTagsRE, "");
24029     },
24030     
24031     /**
24032      * Strips all HTML tags to sort on text only - Case insensitive
24033      * @param {Mixed} s The value being converted
24034      * @return {String} The comparison value
24035      */
24036     asUCText : function(s){
24037         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24038     },
24039     
24040     /**
24041      * Case insensitive string
24042      * @param {Mixed} s The value being converted
24043      * @return {String} The comparison value
24044      */
24045     asUCString : function(s) {
24046         return String(s).toUpperCase();
24047     },
24048     
24049     /**
24050      * Date sorting
24051      * @param {Mixed} s The value being converted
24052      * @return {Number} The comparison value
24053      */
24054     asDate : function(s) {
24055         if(!s){
24056             return 0;
24057         }
24058         if(s instanceof Date){
24059             return s.getTime();
24060         }
24061         return Date.parse(String(s));
24062     },
24063     
24064     /**
24065      * Float sorting
24066      * @param {Mixed} s The value being converted
24067      * @return {Float} The comparison value
24068      */
24069     asFloat : function(s) {
24070         var val = parseFloat(String(s).replace(/,/g, ""));
24071         if(isNaN(val)) {
24072             val = 0;
24073         }
24074         return val;
24075     },
24076     
24077     /**
24078      * Integer sorting
24079      * @param {Mixed} s The value being converted
24080      * @return {Number} The comparison value
24081      */
24082     asInt : function(s) {
24083         var val = parseInt(String(s).replace(/,/g, ""));
24084         if(isNaN(val)) {
24085             val = 0;
24086         }
24087         return val;
24088     }
24089 };/*
24090  * Based on:
24091  * Ext JS Library 1.1.1
24092  * Copyright(c) 2006-2007, Ext JS, LLC.
24093  *
24094  * Originally Released Under LGPL - original licence link has changed is not relivant.
24095  *
24096  * Fork - LGPL
24097  * <script type="text/javascript">
24098  */
24099
24100 /**
24101 * @class Roo.data.Record
24102  * Instances of this class encapsulate both record <em>definition</em> information, and record
24103  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24104  * to access Records cached in an {@link Roo.data.Store} object.<br>
24105  * <p>
24106  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24107  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24108  * objects.<br>
24109  * <p>
24110  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24111  * @constructor
24112  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24113  * {@link #create}. The parameters are the same.
24114  * @param {Array} data An associative Array of data values keyed by the field name.
24115  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24116  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24117  * not specified an integer id is generated.
24118  */
24119 Roo.data.Record = function(data, id){
24120     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24121     this.data = data;
24122 };
24123
24124 /**
24125  * Generate a constructor for a specific record layout.
24126  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24127  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24128  * Each field definition object may contain the following properties: <ul>
24129  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
24130  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24131  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24132  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24133  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24134  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24135  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24136  * this may be omitted.</p></li>
24137  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24138  * <ul><li>auto (Default, implies no conversion)</li>
24139  * <li>string</li>
24140  * <li>int</li>
24141  * <li>float</li>
24142  * <li>boolean</li>
24143  * <li>date</li></ul></p></li>
24144  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24145  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24146  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24147  * by the Reader into an object that will be stored in the Record. It is passed the
24148  * following parameters:<ul>
24149  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24150  * </ul></p></li>
24151  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24152  * </ul>
24153  * <br>usage:<br><pre><code>
24154 var TopicRecord = Roo.data.Record.create(
24155     {name: 'title', mapping: 'topic_title'},
24156     {name: 'author', mapping: 'username'},
24157     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24158     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24159     {name: 'lastPoster', mapping: 'user2'},
24160     {name: 'excerpt', mapping: 'post_text'}
24161 );
24162
24163 var myNewRecord = new TopicRecord({
24164     title: 'Do my job please',
24165     author: 'noobie',
24166     totalPosts: 1,
24167     lastPost: new Date(),
24168     lastPoster: 'Animal',
24169     excerpt: 'No way dude!'
24170 });
24171 myStore.add(myNewRecord);
24172 </code></pre>
24173  * @method create
24174  * @static
24175  */
24176 Roo.data.Record.create = function(o){
24177     var f = function(){
24178         f.superclass.constructor.apply(this, arguments);
24179     };
24180     Roo.extend(f, Roo.data.Record);
24181     var p = f.prototype;
24182     p.fields = new Roo.util.MixedCollection(false, function(field){
24183         return field.name;
24184     });
24185     for(var i = 0, len = o.length; i < len; i++){
24186         p.fields.add(new Roo.data.Field(o[i]));
24187     }
24188     f.getField = function(name){
24189         return p.fields.get(name);  
24190     };
24191     return f;
24192 };
24193
24194 Roo.data.Record.AUTO_ID = 1000;
24195 Roo.data.Record.EDIT = 'edit';
24196 Roo.data.Record.REJECT = 'reject';
24197 Roo.data.Record.COMMIT = 'commit';
24198
24199 Roo.data.Record.prototype = {
24200     /**
24201      * Readonly flag - true if this record has been modified.
24202      * @type Boolean
24203      */
24204     dirty : false,
24205     editing : false,
24206     error: null,
24207     modified: null,
24208
24209     // private
24210     join : function(store){
24211         this.store = store;
24212     },
24213
24214     /**
24215      * Set the named field to the specified value.
24216      * @param {String} name The name of the field to set.
24217      * @param {Object} value The value to set the field to.
24218      */
24219     set : function(name, value){
24220         if(this.data[name] == value){
24221             return;
24222         }
24223         this.dirty = true;
24224         if(!this.modified){
24225             this.modified = {};
24226         }
24227         if(typeof this.modified[name] == 'undefined'){
24228             this.modified[name] = this.data[name];
24229         }
24230         this.data[name] = value;
24231         if(!this.editing && this.store){
24232             this.store.afterEdit(this);
24233         }       
24234     },
24235
24236     /**
24237      * Get the value of the named field.
24238      * @param {String} name The name of the field to get the value of.
24239      * @return {Object} The value of the field.
24240      */
24241     get : function(name){
24242         return this.data[name]; 
24243     },
24244
24245     // private
24246     beginEdit : function(){
24247         this.editing = true;
24248         this.modified = {}; 
24249     },
24250
24251     // private
24252     cancelEdit : function(){
24253         this.editing = false;
24254         delete this.modified;
24255     },
24256
24257     // private
24258     endEdit : function(){
24259         this.editing = false;
24260         if(this.dirty && this.store){
24261             this.store.afterEdit(this);
24262         }
24263     },
24264
24265     /**
24266      * Usually called by the {@link Roo.data.Store} which owns the Record.
24267      * Rejects all changes made to the Record since either creation, or the last commit operation.
24268      * Modified fields are reverted to their original values.
24269      * <p>
24270      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24271      * of reject operations.
24272      */
24273     reject : function(){
24274         var m = this.modified;
24275         for(var n in m){
24276             if(typeof m[n] != "function"){
24277                 this.data[n] = m[n];
24278             }
24279         }
24280         this.dirty = false;
24281         delete this.modified;
24282         this.editing = false;
24283         if(this.store){
24284             this.store.afterReject(this);
24285         }
24286     },
24287
24288     /**
24289      * Usually called by the {@link Roo.data.Store} which owns the Record.
24290      * Commits all changes made to the Record since either creation, or the last commit operation.
24291      * <p>
24292      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24293      * of commit operations.
24294      */
24295     commit : function(){
24296         this.dirty = false;
24297         delete this.modified;
24298         this.editing = false;
24299         if(this.store){
24300             this.store.afterCommit(this);
24301         }
24302     },
24303
24304     // private
24305     hasError : function(){
24306         return this.error != null;
24307     },
24308
24309     // private
24310     clearError : function(){
24311         this.error = null;
24312     },
24313
24314     /**
24315      * Creates a copy of this record.
24316      * @param {String} id (optional) A new record id if you don't want to use this record's id
24317      * @return {Record}
24318      */
24319     copy : function(newId) {
24320         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24321     }
24322 };/*
24323  * Based on:
24324  * Ext JS Library 1.1.1
24325  * Copyright(c) 2006-2007, Ext JS, LLC.
24326  *
24327  * Originally Released Under LGPL - original licence link has changed is not relivant.
24328  *
24329  * Fork - LGPL
24330  * <script type="text/javascript">
24331  */
24332
24333
24334
24335 /**
24336  * @class Roo.data.Store
24337  * @extends Roo.util.Observable
24338  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24339  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24340  * <p>
24341  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
24342  * has no knowledge of the format of the data returned by the Proxy.<br>
24343  * <p>
24344  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24345  * instances from the data object. These records are cached and made available through accessor functions.
24346  * @constructor
24347  * Creates a new Store.
24348  * @param {Object} config A config object containing the objects needed for the Store to access data,
24349  * and read the data into Records.
24350  */
24351 Roo.data.Store = function(config){
24352     this.data = new Roo.util.MixedCollection(false);
24353     this.data.getKey = function(o){
24354         return o.id;
24355     };
24356     this.baseParams = {};
24357     // private
24358     this.paramNames = {
24359         "start" : "start",
24360         "limit" : "limit",
24361         "sort" : "sort",
24362         "dir" : "dir",
24363         "multisort" : "_multisort"
24364     };
24365
24366     if(config && config.data){
24367         this.inlineData = config.data;
24368         delete config.data;
24369     }
24370
24371     Roo.apply(this, config);
24372     
24373     if(this.reader){ // reader passed
24374         this.reader = Roo.factory(this.reader, Roo.data);
24375         this.reader.xmodule = this.xmodule || false;
24376         if(!this.recordType){
24377             this.recordType = this.reader.recordType;
24378         }
24379         if(this.reader.onMetaChange){
24380             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24381         }
24382     }
24383
24384     if(this.recordType){
24385         this.fields = this.recordType.prototype.fields;
24386     }
24387     this.modified = [];
24388
24389     this.addEvents({
24390         /**
24391          * @event datachanged
24392          * Fires when the data cache has changed, and a widget which is using this Store
24393          * as a Record cache should refresh its view.
24394          * @param {Store} this
24395          */
24396         datachanged : true,
24397         /**
24398          * @event metachange
24399          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24400          * @param {Store} this
24401          * @param {Object} meta The JSON metadata
24402          */
24403         metachange : true,
24404         /**
24405          * @event add
24406          * Fires when Records have been added to the Store
24407          * @param {Store} this
24408          * @param {Roo.data.Record[]} records The array of Records added
24409          * @param {Number} index The index at which the record(s) were added
24410          */
24411         add : true,
24412         /**
24413          * @event remove
24414          * Fires when a Record has been removed from the Store
24415          * @param {Store} this
24416          * @param {Roo.data.Record} record The Record that was removed
24417          * @param {Number} index The index at which the record was removed
24418          */
24419         remove : true,
24420         /**
24421          * @event update
24422          * Fires when a Record has been updated
24423          * @param {Store} this
24424          * @param {Roo.data.Record} record The Record that was updated
24425          * @param {String} operation The update operation being performed.  Value may be one of:
24426          * <pre><code>
24427  Roo.data.Record.EDIT
24428  Roo.data.Record.REJECT
24429  Roo.data.Record.COMMIT
24430          * </code></pre>
24431          */
24432         update : true,
24433         /**
24434          * @event clear
24435          * Fires when the data cache has been cleared.
24436          * @param {Store} this
24437          */
24438         clear : true,
24439         /**
24440          * @event beforeload
24441          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24442          * the load action will be canceled.
24443          * @param {Store} this
24444          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24445          */
24446         beforeload : true,
24447         /**
24448          * @event beforeloadadd
24449          * Fires after a new set of Records has been loaded.
24450          * @param {Store} this
24451          * @param {Roo.data.Record[]} records The Records that were loaded
24452          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24453          */
24454         beforeloadadd : true,
24455         /**
24456          * @event load
24457          * Fires after a new set of Records has been loaded, before they are added to the store.
24458          * @param {Store} this
24459          * @param {Roo.data.Record[]} records The Records that were loaded
24460          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24461          * @params {Object} return from reader
24462          */
24463         load : true,
24464         /**
24465          * @event loadexception
24466          * Fires if an exception occurs in the Proxy during loading.
24467          * Called with the signature of the Proxy's "loadexception" event.
24468          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24469          * 
24470          * @param {Proxy} 
24471          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24472          * @param {Object} load options 
24473          * @param {Object} jsonData from your request (normally this contains the Exception)
24474          */
24475         loadexception : true
24476     });
24477     
24478     if(this.proxy){
24479         this.proxy = Roo.factory(this.proxy, Roo.data);
24480         this.proxy.xmodule = this.xmodule || false;
24481         this.relayEvents(this.proxy,  ["loadexception"]);
24482     }
24483     this.sortToggle = {};
24484     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24485
24486     Roo.data.Store.superclass.constructor.call(this);
24487
24488     if(this.inlineData){
24489         this.loadData(this.inlineData);
24490         delete this.inlineData;
24491     }
24492 };
24493
24494 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24495      /**
24496     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24497     * without a remote query - used by combo/forms at present.
24498     */
24499     
24500     /**
24501     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24502     */
24503     /**
24504     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24505     */
24506     /**
24507     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24508     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24509     */
24510     /**
24511     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24512     * on any HTTP request
24513     */
24514     /**
24515     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24516     */
24517     /**
24518     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24519     */
24520     multiSort: false,
24521     /**
24522     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24523     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24524     */
24525     remoteSort : false,
24526
24527     /**
24528     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24529      * loaded or when a record is removed. (defaults to false).
24530     */
24531     pruneModifiedRecords : false,
24532
24533     // private
24534     lastOptions : null,
24535
24536     /**
24537      * Add Records to the Store and fires the add event.
24538      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24539      */
24540     add : function(records){
24541         records = [].concat(records);
24542         for(var i = 0, len = records.length; i < len; i++){
24543             records[i].join(this);
24544         }
24545         var index = this.data.length;
24546         this.data.addAll(records);
24547         this.fireEvent("add", this, records, index);
24548     },
24549
24550     /**
24551      * Remove a Record from the Store and fires the remove event.
24552      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24553      */
24554     remove : function(record){
24555         var index = this.data.indexOf(record);
24556         this.data.removeAt(index);
24557  
24558         if(this.pruneModifiedRecords){
24559             this.modified.remove(record);
24560         }
24561         this.fireEvent("remove", this, record, index);
24562     },
24563
24564     /**
24565      * Remove all Records from the Store and fires the clear event.
24566      */
24567     removeAll : function(){
24568         this.data.clear();
24569         if(this.pruneModifiedRecords){
24570             this.modified = [];
24571         }
24572         this.fireEvent("clear", this);
24573     },
24574
24575     /**
24576      * Inserts Records to the Store at the given index and fires the add event.
24577      * @param {Number} index The start index at which to insert the passed Records.
24578      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24579      */
24580     insert : function(index, records){
24581         records = [].concat(records);
24582         for(var i = 0, len = records.length; i < len; i++){
24583             this.data.insert(index, records[i]);
24584             records[i].join(this);
24585         }
24586         this.fireEvent("add", this, records, index);
24587     },
24588
24589     /**
24590      * Get the index within the cache of the passed Record.
24591      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24592      * @return {Number} The index of the passed Record. Returns -1 if not found.
24593      */
24594     indexOf : function(record){
24595         return this.data.indexOf(record);
24596     },
24597
24598     /**
24599      * Get the index within the cache of the Record with the passed id.
24600      * @param {String} id The id of the Record to find.
24601      * @return {Number} The index of the Record. Returns -1 if not found.
24602      */
24603     indexOfId : function(id){
24604         return this.data.indexOfKey(id);
24605     },
24606
24607     /**
24608      * Get the Record with the specified id.
24609      * @param {String} id The id of the Record to find.
24610      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24611      */
24612     getById : function(id){
24613         return this.data.key(id);
24614     },
24615
24616     /**
24617      * Get the Record at the specified index.
24618      * @param {Number} index The index of the Record to find.
24619      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24620      */
24621     getAt : function(index){
24622         return this.data.itemAt(index);
24623     },
24624
24625     /**
24626      * Returns a range of Records between specified indices.
24627      * @param {Number} startIndex (optional) The starting index (defaults to 0)
24628      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24629      * @return {Roo.data.Record[]} An array of Records
24630      */
24631     getRange : function(start, end){
24632         return this.data.getRange(start, end);
24633     },
24634
24635     // private
24636     storeOptions : function(o){
24637         o = Roo.apply({}, o);
24638         delete o.callback;
24639         delete o.scope;
24640         this.lastOptions = o;
24641     },
24642
24643     /**
24644      * Loads the Record cache from the configured Proxy using the configured Reader.
24645      * <p>
24646      * If using remote paging, then the first load call must specify the <em>start</em>
24647      * and <em>limit</em> properties in the options.params property to establish the initial
24648      * position within the dataset, and the number of Records to cache on each read from the Proxy.
24649      * <p>
24650      * <strong>It is important to note that for remote data sources, loading is asynchronous,
24651      * and this call will return before the new data has been loaded. Perform any post-processing
24652      * in a callback function, or in a "load" event handler.</strong>
24653      * <p>
24654      * @param {Object} options An object containing properties which control loading options:<ul>
24655      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24656      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
24657      * <pre>
24658                 {
24659                     data : data,  // array of key=>value data like JsonReader
24660                     total : data.length,
24661                     success : true
24662                     
24663                 }
24664         </pre>
24665             }.</li>
24666      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24667      * passed the following arguments:<ul>
24668      * <li>r : Roo.data.Record[]</li>
24669      * <li>options: Options object from the load call</li>
24670      * <li>success: Boolean success indicator</li></ul></li>
24671      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24672      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24673      * </ul>
24674      */
24675     load : function(options){
24676         options = options || {};
24677         if(this.fireEvent("beforeload", this, options) !== false){
24678             this.storeOptions(options);
24679             var p = Roo.apply(options.params || {}, this.baseParams);
24680             // if meta was not loaded from remote source.. try requesting it.
24681             if (!this.reader.metaFromRemote) {
24682                 p._requestMeta = 1;
24683             }
24684             if(this.sortInfo && this.remoteSort){
24685                 var pn = this.paramNames;
24686                 p[pn["sort"]] = this.sortInfo.field;
24687                 p[pn["dir"]] = this.sortInfo.direction;
24688             }
24689             if (this.multiSort) {
24690                 var pn = this.paramNames;
24691                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
24692             }
24693             
24694             this.proxy.load(p, this.reader, this.loadRecords, this, options);
24695         }
24696     },
24697
24698     /**
24699      * Reloads the Record cache from the configured Proxy using the configured Reader and
24700      * the options from the last load operation performed.
24701      * @param {Object} options (optional) An object containing properties which may override the options
24702      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
24703      * the most recently used options are reused).
24704      */
24705     reload : function(options){
24706         this.load(Roo.applyIf(options||{}, this.lastOptions));
24707     },
24708
24709     // private
24710     // Called as a callback by the Reader during a load operation.
24711     loadRecords : function(o, options, success){
24712          
24713         if(!o){
24714             if(success !== false){
24715                 this.fireEvent("load", this, [], options, o);
24716             }
24717             if(options.callback){
24718                 options.callback.call(options.scope || this, [], options, false);
24719             }
24720             return;
24721         }
24722         // if data returned failure - throw an exception.
24723         if (o.success === false) {
24724             // show a message if no listener is registered.
24725             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
24726                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
24727             }
24728             // loadmask wil be hooked into this..
24729             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
24730             return;
24731         }
24732         var r = o.records, t = o.totalRecords || r.length;
24733         
24734         this.fireEvent("beforeloadadd", this, r, options, o);
24735         
24736         if(!options || options.add !== true){
24737             if(this.pruneModifiedRecords){
24738                 this.modified = [];
24739             }
24740             for(var i = 0, len = r.length; i < len; i++){
24741                 r[i].join(this);
24742             }
24743             if(this.snapshot){
24744                 this.data = this.snapshot;
24745                 delete this.snapshot;
24746             }
24747             this.data.clear();
24748             this.data.addAll(r);
24749             this.totalLength = t;
24750             this.applySort();
24751             this.fireEvent("datachanged", this);
24752         }else{
24753             this.totalLength = Math.max(t, this.data.length+r.length);
24754             this.add(r);
24755         }
24756         
24757         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
24758                 
24759             var e = new Roo.data.Record({});
24760
24761             e.set(this.parent.displayField, this.parent.emptyTitle);
24762             e.set(this.parent.valueField, '');
24763
24764             this.insert(0, e);
24765         }
24766             
24767         this.fireEvent("load", this, r, options, o);
24768         if(options.callback){
24769             options.callback.call(options.scope || this, r, options, true);
24770         }
24771     },
24772
24773
24774     /**
24775      * Loads data from a passed data block. A Reader which understands the format of the data
24776      * must have been configured in the constructor.
24777      * @param {Object} data The data block from which to read the Records.  The format of the data expected
24778      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
24779      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
24780      */
24781     loadData : function(o, append){
24782         var r = this.reader.readRecords(o);
24783         this.loadRecords(r, {add: append}, true);
24784     },
24785     
24786      /**
24787      * using 'cn' the nested child reader read the child array into it's child stores.
24788      * @param {Object} rec The record with a 'children array
24789      */
24790     loadDataFromChildren : function(rec)
24791     {
24792         this.loadData(this.reader.toLoadData(rec));
24793     },
24794     
24795
24796     /**
24797      * Gets the number of cached records.
24798      * <p>
24799      * <em>If using paging, this may not be the total size of the dataset. If the data object
24800      * used by the Reader contains the dataset size, then the getTotalCount() function returns
24801      * the data set size</em>
24802      */
24803     getCount : function(){
24804         return this.data.length || 0;
24805     },
24806
24807     /**
24808      * Gets the total number of records in the dataset as returned by the server.
24809      * <p>
24810      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
24811      * the dataset size</em>
24812      */
24813     getTotalCount : function(){
24814         return this.totalLength || 0;
24815     },
24816
24817     /**
24818      * Returns the sort state of the Store as an object with two properties:
24819      * <pre><code>
24820  field {String} The name of the field by which the Records are sorted
24821  direction {String} The sort order, "ASC" or "DESC"
24822      * </code></pre>
24823      */
24824     getSortState : function(){
24825         return this.sortInfo;
24826     },
24827
24828     // private
24829     applySort : function(){
24830         if(this.sortInfo && !this.remoteSort){
24831             var s = this.sortInfo, f = s.field;
24832             var st = this.fields.get(f).sortType;
24833             var fn = function(r1, r2){
24834                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
24835                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
24836             };
24837             this.data.sort(s.direction, fn);
24838             if(this.snapshot && this.snapshot != this.data){
24839                 this.snapshot.sort(s.direction, fn);
24840             }
24841         }
24842     },
24843
24844     /**
24845      * Sets the default sort column and order to be used by the next load operation.
24846      * @param {String} fieldName The name of the field to sort by.
24847      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24848      */
24849     setDefaultSort : function(field, dir){
24850         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
24851     },
24852
24853     /**
24854      * Sort the Records.
24855      * If remote sorting is used, the sort is performed on the server, and the cache is
24856      * reloaded. If local sorting is used, the cache is sorted internally.
24857      * @param {String} fieldName The name of the field to sort by.
24858      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24859      */
24860     sort : function(fieldName, dir){
24861         var f = this.fields.get(fieldName);
24862         if(!dir){
24863             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
24864             
24865             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
24866                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
24867             }else{
24868                 dir = f.sortDir;
24869             }
24870         }
24871         this.sortToggle[f.name] = dir;
24872         this.sortInfo = {field: f.name, direction: dir};
24873         if(!this.remoteSort){
24874             this.applySort();
24875             this.fireEvent("datachanged", this);
24876         }else{
24877             this.load(this.lastOptions);
24878         }
24879     },
24880
24881     /**
24882      * Calls the specified function for each of the Records in the cache.
24883      * @param {Function} fn The function to call. The Record is passed as the first parameter.
24884      * Returning <em>false</em> aborts and exits the iteration.
24885      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
24886      */
24887     each : function(fn, scope){
24888         this.data.each(fn, scope);
24889     },
24890
24891     /**
24892      * Gets all records modified since the last commit.  Modified records are persisted across load operations
24893      * (e.g., during paging).
24894      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
24895      */
24896     getModifiedRecords : function(){
24897         return this.modified;
24898     },
24899
24900     // private
24901     createFilterFn : function(property, value, anyMatch){
24902         if(!value.exec){ // not a regex
24903             value = String(value);
24904             if(value.length == 0){
24905                 return false;
24906             }
24907             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24908         }
24909         return function(r){
24910             return value.test(r.data[property]);
24911         };
24912     },
24913
24914     /**
24915      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24916      * @param {String} property A field on your records
24917      * @param {Number} start The record index to start at (defaults to 0)
24918      * @param {Number} end The last record index to include (defaults to length - 1)
24919      * @return {Number} The sum
24920      */
24921     sum : function(property, start, end){
24922         var rs = this.data.items, v = 0;
24923         start = start || 0;
24924         end = (end || end === 0) ? end : rs.length-1;
24925
24926         for(var i = start; i <= end; i++){
24927             v += (rs[i].data[property] || 0);
24928         }
24929         return v;
24930     },
24931
24932     /**
24933      * Filter the records by a specified property.
24934      * @param {String} field A field on your records
24935      * @param {String/RegExp} value Either a string that the field
24936      * should start with or a RegExp to test against the field
24937      * @param {Boolean} anyMatch True to match any part not just the beginning
24938      */
24939     filter : function(property, value, anyMatch){
24940         var fn = this.createFilterFn(property, value, anyMatch);
24941         return fn ? this.filterBy(fn) : this.clearFilter();
24942     },
24943
24944     /**
24945      * Filter by a function. The specified function will be called with each
24946      * record in this data source. If the function returns true the record is included,
24947      * otherwise it is filtered.
24948      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24949      * @param {Object} scope (optional) The scope of the function (defaults to this)
24950      */
24951     filterBy : function(fn, scope){
24952         this.snapshot = this.snapshot || this.data;
24953         this.data = this.queryBy(fn, scope||this);
24954         this.fireEvent("datachanged", this);
24955     },
24956
24957     /**
24958      * Query the records by a specified property.
24959      * @param {String} field A field on your records
24960      * @param {String/RegExp} value Either a string that the field
24961      * should start with or a RegExp to test against the field
24962      * @param {Boolean} anyMatch True to match any part not just the beginning
24963      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24964      */
24965     query : function(property, value, anyMatch){
24966         var fn = this.createFilterFn(property, value, anyMatch);
24967         return fn ? this.queryBy(fn) : this.data.clone();
24968     },
24969
24970     /**
24971      * Query by a function. The specified function will be called with each
24972      * record in this data source. If the function returns true the record is included
24973      * in the results.
24974      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24975      * @param {Object} scope (optional) The scope of the function (defaults to this)
24976       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24977      **/
24978     queryBy : function(fn, scope){
24979         var data = this.snapshot || this.data;
24980         return data.filterBy(fn, scope||this);
24981     },
24982
24983     /**
24984      * Collects unique values for a particular dataIndex from this store.
24985      * @param {String} dataIndex The property to collect
24986      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24987      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24988      * @return {Array} An array of the unique values
24989      **/
24990     collect : function(dataIndex, allowNull, bypassFilter){
24991         var d = (bypassFilter === true && this.snapshot) ?
24992                 this.snapshot.items : this.data.items;
24993         var v, sv, r = [], l = {};
24994         for(var i = 0, len = d.length; i < len; i++){
24995             v = d[i].data[dataIndex];
24996             sv = String(v);
24997             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24998                 l[sv] = true;
24999                 r[r.length] = v;
25000             }
25001         }
25002         return r;
25003     },
25004
25005     /**
25006      * Revert to a view of the Record cache with no filtering applied.
25007      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25008      */
25009     clearFilter : function(suppressEvent){
25010         if(this.snapshot && this.snapshot != this.data){
25011             this.data = this.snapshot;
25012             delete this.snapshot;
25013             if(suppressEvent !== true){
25014                 this.fireEvent("datachanged", this);
25015             }
25016         }
25017     },
25018
25019     // private
25020     afterEdit : function(record){
25021         if(this.modified.indexOf(record) == -1){
25022             this.modified.push(record);
25023         }
25024         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25025     },
25026     
25027     // private
25028     afterReject : function(record){
25029         this.modified.remove(record);
25030         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25031     },
25032
25033     // private
25034     afterCommit : function(record){
25035         this.modified.remove(record);
25036         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25037     },
25038
25039     /**
25040      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25041      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25042      */
25043     commitChanges : function(){
25044         var m = this.modified.slice(0);
25045         this.modified = [];
25046         for(var i = 0, len = m.length; i < len; i++){
25047             m[i].commit();
25048         }
25049     },
25050
25051     /**
25052      * Cancel outstanding changes on all changed records.
25053      */
25054     rejectChanges : function(){
25055         var m = this.modified.slice(0);
25056         this.modified = [];
25057         for(var i = 0, len = m.length; i < len; i++){
25058             m[i].reject();
25059         }
25060     },
25061
25062     onMetaChange : function(meta, rtype, o){
25063         this.recordType = rtype;
25064         this.fields = rtype.prototype.fields;
25065         delete this.snapshot;
25066         this.sortInfo = meta.sortInfo || this.sortInfo;
25067         this.modified = [];
25068         this.fireEvent('metachange', this, this.reader.meta);
25069     },
25070     
25071     moveIndex : function(data, type)
25072     {
25073         var index = this.indexOf(data);
25074         
25075         var newIndex = index + type;
25076         
25077         this.remove(data);
25078         
25079         this.insert(newIndex, data);
25080         
25081     }
25082 });/*
25083  * Based on:
25084  * Ext JS Library 1.1.1
25085  * Copyright(c) 2006-2007, Ext JS, LLC.
25086  *
25087  * Originally Released Under LGPL - original licence link has changed is not relivant.
25088  *
25089  * Fork - LGPL
25090  * <script type="text/javascript">
25091  */
25092
25093 /**
25094  * @class Roo.data.SimpleStore
25095  * @extends Roo.data.Store
25096  * Small helper class to make creating Stores from Array data easier.
25097  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25098  * @cfg {Array} fields An array of field definition objects, or field name strings.
25099  * @cfg {Object} an existing reader (eg. copied from another store)
25100  * @cfg {Array} data The multi-dimensional array of data
25101  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25102  * @cfg {Roo.data.Reader} reader  [not-required] 
25103  * @constructor
25104  * @param {Object} config
25105  */
25106 Roo.data.SimpleStore = function(config)
25107 {
25108     Roo.data.SimpleStore.superclass.constructor.call(this, {
25109         isLocal : true,
25110         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25111                 id: config.id
25112             },
25113             Roo.data.Record.create(config.fields)
25114         ),
25115         proxy : new Roo.data.MemoryProxy(config.data)
25116     });
25117     this.load();
25118 };
25119 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25120  * Based on:
25121  * Ext JS Library 1.1.1
25122  * Copyright(c) 2006-2007, Ext JS, LLC.
25123  *
25124  * Originally Released Under LGPL - original licence link has changed is not relivant.
25125  *
25126  * Fork - LGPL
25127  * <script type="text/javascript">
25128  */
25129
25130 /**
25131 /**
25132  * @extends Roo.data.Store
25133  * @class Roo.data.JsonStore
25134  * Small helper class to make creating Stores for JSON data easier. <br/>
25135 <pre><code>
25136 var store = new Roo.data.JsonStore({
25137     url: 'get-images.php',
25138     root: 'images',
25139     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25140 });
25141 </code></pre>
25142  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25143  * JsonReader and HttpProxy (unless inline data is provided).</b>
25144  * @cfg {Array} fields An array of field definition objects, or field name strings.
25145  * @constructor
25146  * @param {Object} config
25147  */
25148 Roo.data.JsonStore = function(c){
25149     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25150         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25151         reader: new Roo.data.JsonReader(c, c.fields)
25152     }));
25153 };
25154 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25155  * Based on:
25156  * Ext JS Library 1.1.1
25157  * Copyright(c) 2006-2007, Ext JS, LLC.
25158  *
25159  * Originally Released Under LGPL - original licence link has changed is not relivant.
25160  *
25161  * Fork - LGPL
25162  * <script type="text/javascript">
25163  */
25164
25165  
25166 Roo.data.Field = function(config){
25167     if(typeof config == "string"){
25168         config = {name: config};
25169     }
25170     Roo.apply(this, config);
25171     
25172     if(!this.type){
25173         this.type = "auto";
25174     }
25175     
25176     var st = Roo.data.SortTypes;
25177     // named sortTypes are supported, here we look them up
25178     if(typeof this.sortType == "string"){
25179         this.sortType = st[this.sortType];
25180     }
25181     
25182     // set default sortType for strings and dates
25183     if(!this.sortType){
25184         switch(this.type){
25185             case "string":
25186                 this.sortType = st.asUCString;
25187                 break;
25188             case "date":
25189                 this.sortType = st.asDate;
25190                 break;
25191             default:
25192                 this.sortType = st.none;
25193         }
25194     }
25195
25196     // define once
25197     var stripRe = /[\$,%]/g;
25198
25199     // prebuilt conversion function for this field, instead of
25200     // switching every time we're reading a value
25201     if(!this.convert){
25202         var cv, dateFormat = this.dateFormat;
25203         switch(this.type){
25204             case "":
25205             case "auto":
25206             case undefined:
25207                 cv = function(v){ return v; };
25208                 break;
25209             case "string":
25210                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25211                 break;
25212             case "int":
25213                 cv = function(v){
25214                     return v !== undefined && v !== null && v !== '' ?
25215                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25216                     };
25217                 break;
25218             case "float":
25219                 cv = function(v){
25220                     return v !== undefined && v !== null && v !== '' ?
25221                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25222                     };
25223                 break;
25224             case "bool":
25225             case "boolean":
25226                 cv = function(v){ return v === true || v === "true" || v == 1; };
25227                 break;
25228             case "date":
25229                 cv = function(v){
25230                     if(!v){
25231                         return '';
25232                     }
25233                     if(v instanceof Date){
25234                         return v;
25235                     }
25236                     if(dateFormat){
25237                         if(dateFormat == "timestamp"){
25238                             return new Date(v*1000);
25239                         }
25240                         return Date.parseDate(v, dateFormat);
25241                     }
25242                     var parsed = Date.parse(v);
25243                     return parsed ? new Date(parsed) : null;
25244                 };
25245              break;
25246             
25247         }
25248         this.convert = cv;
25249     }
25250 };
25251
25252 Roo.data.Field.prototype = {
25253     dateFormat: null,
25254     defaultValue: "",
25255     mapping: null,
25256     sortType : null,
25257     sortDir : "ASC"
25258 };/*
25259  * Based on:
25260  * Ext JS Library 1.1.1
25261  * Copyright(c) 2006-2007, Ext JS, LLC.
25262  *
25263  * Originally Released Under LGPL - original licence link has changed is not relivant.
25264  *
25265  * Fork - LGPL
25266  * <script type="text/javascript">
25267  */
25268  
25269 // Base class for reading structured data from a data source.  This class is intended to be
25270 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25271
25272 /**
25273  * @class Roo.data.DataReader
25274  * @abstract
25275  * Base class for reading structured data from a data source.  This class is intended to be
25276  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25277  */
25278
25279 Roo.data.DataReader = function(meta, recordType){
25280     
25281     this.meta = meta;
25282     
25283     this.recordType = recordType instanceof Array ? 
25284         Roo.data.Record.create(recordType) : recordType;
25285 };
25286
25287 Roo.data.DataReader.prototype = {
25288     
25289     
25290     readerType : 'Data',
25291      /**
25292      * Create an empty record
25293      * @param {Object} data (optional) - overlay some values
25294      * @return {Roo.data.Record} record created.
25295      */
25296     newRow :  function(d) {
25297         var da =  {};
25298         this.recordType.prototype.fields.each(function(c) {
25299             switch( c.type) {
25300                 case 'int' : da[c.name] = 0; break;
25301                 case 'date' : da[c.name] = new Date(); break;
25302                 case 'float' : da[c.name] = 0.0; break;
25303                 case 'boolean' : da[c.name] = false; break;
25304                 default : da[c.name] = ""; break;
25305             }
25306             
25307         });
25308         return new this.recordType(Roo.apply(da, d));
25309     }
25310     
25311     
25312 };/*
25313  * Based on:
25314  * Ext JS Library 1.1.1
25315  * Copyright(c) 2006-2007, Ext JS, LLC.
25316  *
25317  * Originally Released Under LGPL - original licence link has changed is not relivant.
25318  *
25319  * Fork - LGPL
25320  * <script type="text/javascript">
25321  */
25322
25323 /**
25324  * @class Roo.data.DataProxy
25325  * @extends Roo.util.Observable
25326  * @abstract
25327  * This class is an abstract base class for implementations which provide retrieval of
25328  * unformatted data objects.<br>
25329  * <p>
25330  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25331  * (of the appropriate type which knows how to parse the data object) to provide a block of
25332  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25333  * <p>
25334  * Custom implementations must implement the load method as described in
25335  * {@link Roo.data.HttpProxy#load}.
25336  */
25337 Roo.data.DataProxy = function(){
25338     this.addEvents({
25339         /**
25340          * @event beforeload
25341          * Fires before a network request is made to retrieve a data object.
25342          * @param {Object} This DataProxy object.
25343          * @param {Object} params The params parameter to the load function.
25344          */
25345         beforeload : true,
25346         /**
25347          * @event load
25348          * Fires before the load method's callback is called.
25349          * @param {Object} This DataProxy object.
25350          * @param {Object} o The data object.
25351          * @param {Object} arg The callback argument object passed to the load function.
25352          */
25353         load : true,
25354         /**
25355          * @event loadexception
25356          * Fires if an Exception occurs during data retrieval.
25357          * @param {Object} This DataProxy object.
25358          * @param {Object} o The data object.
25359          * @param {Object} arg The callback argument object passed to the load function.
25360          * @param {Object} e The Exception.
25361          */
25362         loadexception : true
25363     });
25364     Roo.data.DataProxy.superclass.constructor.call(this);
25365 };
25366
25367 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25368
25369     /**
25370      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25371      */
25372 /*
25373  * Based on:
25374  * Ext JS Library 1.1.1
25375  * Copyright(c) 2006-2007, Ext JS, LLC.
25376  *
25377  * Originally Released Under LGPL - original licence link has changed is not relivant.
25378  *
25379  * Fork - LGPL
25380  * <script type="text/javascript">
25381  */
25382 /**
25383  * @class Roo.data.MemoryProxy
25384  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25385  * to the Reader when its load method is called.
25386  * @constructor
25387  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25388  */
25389 Roo.data.MemoryProxy = function(data){
25390     if (data.data) {
25391         data = data.data;
25392     }
25393     Roo.data.MemoryProxy.superclass.constructor.call(this);
25394     this.data = data;
25395 };
25396
25397 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25398     
25399     /**
25400      * Load data from the requested source (in this case an in-memory
25401      * data object passed to the constructor), read the data object into
25402      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25403      * process that block using the passed callback.
25404      * @param {Object} params This parameter is not used by the MemoryProxy class.
25405      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25406      * object into a block of Roo.data.Records.
25407      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25408      * The function must be passed <ul>
25409      * <li>The Record block object</li>
25410      * <li>The "arg" argument from the load function</li>
25411      * <li>A boolean success indicator</li>
25412      * </ul>
25413      * @param {Object} scope The scope in which to call the callback
25414      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25415      */
25416     load : function(params, reader, callback, scope, arg){
25417         params = params || {};
25418         var result;
25419         try {
25420             result = reader.readRecords(params.data ? params.data :this.data);
25421         }catch(e){
25422             this.fireEvent("loadexception", this, arg, null, e);
25423             callback.call(scope, null, arg, false);
25424             return;
25425         }
25426         callback.call(scope, result, arg, true);
25427     },
25428     
25429     // private
25430     update : function(params, records){
25431         
25432     }
25433 });/*
25434  * Based on:
25435  * Ext JS Library 1.1.1
25436  * Copyright(c) 2006-2007, Ext JS, LLC.
25437  *
25438  * Originally Released Under LGPL - original licence link has changed is not relivant.
25439  *
25440  * Fork - LGPL
25441  * <script type="text/javascript">
25442  */
25443 /**
25444  * @class Roo.data.HttpProxy
25445  * @extends Roo.data.DataProxy
25446  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25447  * configured to reference a certain URL.<br><br>
25448  * <p>
25449  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25450  * from which the running page was served.<br><br>
25451  * <p>
25452  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25453  * <p>
25454  * Be aware that to enable the browser to parse an XML document, the server must set
25455  * the Content-Type header in the HTTP response to "text/xml".
25456  * @constructor
25457  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25458  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25459  * will be used to make the request.
25460  */
25461 Roo.data.HttpProxy = function(conn){
25462     Roo.data.HttpProxy.superclass.constructor.call(this);
25463     // is conn a conn config or a real conn?
25464     this.conn = conn;
25465     this.useAjax = !conn || !conn.events;
25466   
25467 };
25468
25469 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25470     // thse are take from connection...
25471     
25472     /**
25473      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25474      */
25475     /**
25476      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25477      * extra parameters to each request made by this object. (defaults to undefined)
25478      */
25479     /**
25480      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25481      *  to each request made by this object. (defaults to undefined)
25482      */
25483     /**
25484      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
25485      */
25486     /**
25487      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25488      */
25489      /**
25490      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25491      * @type Boolean
25492      */
25493   
25494
25495     /**
25496      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25497      * @type Boolean
25498      */
25499     /**
25500      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25501      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25502      * a finer-grained basis than the DataProxy events.
25503      */
25504     getConnection : function(){
25505         return this.useAjax ? Roo.Ajax : this.conn;
25506     },
25507
25508     /**
25509      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25510      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25511      * process that block using the passed callback.
25512      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25513      * for the request to the remote server.
25514      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25515      * object into a block of Roo.data.Records.
25516      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25517      * The function must be passed <ul>
25518      * <li>The Record block object</li>
25519      * <li>The "arg" argument from the load function</li>
25520      * <li>A boolean success indicator</li>
25521      * </ul>
25522      * @param {Object} scope The scope in which to call the callback
25523      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25524      */
25525     load : function(params, reader, callback, scope, arg){
25526         if(this.fireEvent("beforeload", this, params) !== false){
25527             var  o = {
25528                 params : params || {},
25529                 request: {
25530                     callback : callback,
25531                     scope : scope,
25532                     arg : arg
25533                 },
25534                 reader: reader,
25535                 callback : this.loadResponse,
25536                 scope: this
25537             };
25538             if(this.useAjax){
25539                 Roo.applyIf(o, this.conn);
25540                 if(this.activeRequest){
25541                     Roo.Ajax.abort(this.activeRequest);
25542                 }
25543                 this.activeRequest = Roo.Ajax.request(o);
25544             }else{
25545                 this.conn.request(o);
25546             }
25547         }else{
25548             callback.call(scope||this, null, arg, false);
25549         }
25550     },
25551
25552     // private
25553     loadResponse : function(o, success, response){
25554         delete this.activeRequest;
25555         if(!success){
25556             this.fireEvent("loadexception", this, o, response);
25557             o.request.callback.call(o.request.scope, null, o.request.arg, false);
25558             return;
25559         }
25560         var result;
25561         try {
25562             result = o.reader.read(response);
25563         }catch(e){
25564             o.success = false;
25565             o.raw = { errorMsg : response.responseText };
25566             this.fireEvent("loadexception", this, o, response, e);
25567             o.request.callback.call(o.request.scope, o, o.request.arg, false);
25568             return;
25569         }
25570         
25571         this.fireEvent("load", this, o, o.request.arg);
25572         o.request.callback.call(o.request.scope, result, o.request.arg, true);
25573     },
25574
25575     // private
25576     update : function(dataSet){
25577
25578     },
25579
25580     // private
25581     updateResponse : function(dataSet){
25582
25583     }
25584 });/*
25585  * Based on:
25586  * Ext JS Library 1.1.1
25587  * Copyright(c) 2006-2007, Ext JS, LLC.
25588  *
25589  * Originally Released Under LGPL - original licence link has changed is not relivant.
25590  *
25591  * Fork - LGPL
25592  * <script type="text/javascript">
25593  */
25594
25595 /**
25596  * @class Roo.data.ScriptTagProxy
25597  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25598  * other than the originating domain of the running page.<br><br>
25599  * <p>
25600  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
25601  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25602  * <p>
25603  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25604  * source code that is used as the source inside a &lt;script> tag.<br><br>
25605  * <p>
25606  * In order for the browser to process the returned data, the server must wrap the data object
25607  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25608  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25609  * depending on whether the callback name was passed:
25610  * <p>
25611  * <pre><code>
25612 boolean scriptTag = false;
25613 String cb = request.getParameter("callback");
25614 if (cb != null) {
25615     scriptTag = true;
25616     response.setContentType("text/javascript");
25617 } else {
25618     response.setContentType("application/x-json");
25619 }
25620 Writer out = response.getWriter();
25621 if (scriptTag) {
25622     out.write(cb + "(");
25623 }
25624 out.print(dataBlock.toJsonString());
25625 if (scriptTag) {
25626     out.write(");");
25627 }
25628 </pre></code>
25629  *
25630  * @constructor
25631  * @param {Object} config A configuration object.
25632  */
25633 Roo.data.ScriptTagProxy = function(config){
25634     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25635     Roo.apply(this, config);
25636     this.head = document.getElementsByTagName("head")[0];
25637 };
25638
25639 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25640
25641 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25642     /**
25643      * @cfg {String} url The URL from which to request the data object.
25644      */
25645     /**
25646      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25647      */
25648     timeout : 30000,
25649     /**
25650      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25651      * the server the name of the callback function set up by the load call to process the returned data object.
25652      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25653      * javascript output which calls this named function passing the data object as its only parameter.
25654      */
25655     callbackParam : "callback",
25656     /**
25657      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25658      * name to the request.
25659      */
25660     nocache : true,
25661
25662     /**
25663      * Load data from the configured URL, read the data object into
25664      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25665      * process that block using the passed callback.
25666      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25667      * for the request to the remote server.
25668      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25669      * object into a block of Roo.data.Records.
25670      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25671      * The function must be passed <ul>
25672      * <li>The Record block object</li>
25673      * <li>The "arg" argument from the load function</li>
25674      * <li>A boolean success indicator</li>
25675      * </ul>
25676      * @param {Object} scope The scope in which to call the callback
25677      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25678      */
25679     load : function(params, reader, callback, scope, arg){
25680         if(this.fireEvent("beforeload", this, params) !== false){
25681
25682             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
25683
25684             var url = this.url;
25685             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
25686             if(this.nocache){
25687                 url += "&_dc=" + (new Date().getTime());
25688             }
25689             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
25690             var trans = {
25691                 id : transId,
25692                 cb : "stcCallback"+transId,
25693                 scriptId : "stcScript"+transId,
25694                 params : params,
25695                 arg : arg,
25696                 url : url,
25697                 callback : callback,
25698                 scope : scope,
25699                 reader : reader
25700             };
25701             var conn = this;
25702
25703             window[trans.cb] = function(o){
25704                 conn.handleResponse(o, trans);
25705             };
25706
25707             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
25708
25709             if(this.autoAbort !== false){
25710                 this.abort();
25711             }
25712
25713             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
25714
25715             var script = document.createElement("script");
25716             script.setAttribute("src", url);
25717             script.setAttribute("type", "text/javascript");
25718             script.setAttribute("id", trans.scriptId);
25719             this.head.appendChild(script);
25720
25721             this.trans = trans;
25722         }else{
25723             callback.call(scope||this, null, arg, false);
25724         }
25725     },
25726
25727     // private
25728     isLoading : function(){
25729         return this.trans ? true : false;
25730     },
25731
25732     /**
25733      * Abort the current server request.
25734      */
25735     abort : function(){
25736         if(this.isLoading()){
25737             this.destroyTrans(this.trans);
25738         }
25739     },
25740
25741     // private
25742     destroyTrans : function(trans, isLoaded){
25743         this.head.removeChild(document.getElementById(trans.scriptId));
25744         clearTimeout(trans.timeoutId);
25745         if(isLoaded){
25746             window[trans.cb] = undefined;
25747             try{
25748                 delete window[trans.cb];
25749             }catch(e){}
25750         }else{
25751             // if hasn't been loaded, wait for load to remove it to prevent script error
25752             window[trans.cb] = function(){
25753                 window[trans.cb] = undefined;
25754                 try{
25755                     delete window[trans.cb];
25756                 }catch(e){}
25757             };
25758         }
25759     },
25760
25761     // private
25762     handleResponse : function(o, trans){
25763         this.trans = false;
25764         this.destroyTrans(trans, true);
25765         var result;
25766         try {
25767             result = trans.reader.readRecords(o);
25768         }catch(e){
25769             this.fireEvent("loadexception", this, o, trans.arg, e);
25770             trans.callback.call(trans.scope||window, null, trans.arg, false);
25771             return;
25772         }
25773         this.fireEvent("load", this, o, trans.arg);
25774         trans.callback.call(trans.scope||window, result, trans.arg, true);
25775     },
25776
25777     // private
25778     handleFailure : function(trans){
25779         this.trans = false;
25780         this.destroyTrans(trans, false);
25781         this.fireEvent("loadexception", this, null, trans.arg);
25782         trans.callback.call(trans.scope||window, null, trans.arg, false);
25783     }
25784 });/*
25785  * Based on:
25786  * Ext JS Library 1.1.1
25787  * Copyright(c) 2006-2007, Ext JS, LLC.
25788  *
25789  * Originally Released Under LGPL - original licence link has changed is not relivant.
25790  *
25791  * Fork - LGPL
25792  * <script type="text/javascript">
25793  */
25794
25795 /**
25796  * @class Roo.data.JsonReader
25797  * @extends Roo.data.DataReader
25798  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
25799  * based on mappings in a provided Roo.data.Record constructor.
25800  * 
25801  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
25802  * in the reply previously. 
25803  * 
25804  * <p>
25805  * Example code:
25806  * <pre><code>
25807 var RecordDef = Roo.data.Record.create([
25808     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25809     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25810 ]);
25811 var myReader = new Roo.data.JsonReader({
25812     totalProperty: "results",    // The property which contains the total dataset size (optional)
25813     root: "rows",                // The property which contains an Array of row objects
25814     id: "id"                     // The property within each row object that provides an ID for the record (optional)
25815 }, RecordDef);
25816 </code></pre>
25817  * <p>
25818  * This would consume a JSON file like this:
25819  * <pre><code>
25820 { 'results': 2, 'rows': [
25821     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
25822     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
25823 }
25824 </code></pre>
25825  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
25826  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25827  * paged from the remote server.
25828  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
25829  * @cfg {String} root name of the property which contains the Array of row objects.
25830  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25831  * @cfg {Array} fields Array of field definition objects
25832  * @constructor
25833  * Create a new JsonReader
25834  * @param {Object} meta Metadata configuration options
25835  * @param {Object} recordType Either an Array of field definition objects,
25836  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
25837  */
25838 Roo.data.JsonReader = function(meta, recordType){
25839     
25840     meta = meta || {};
25841     // set some defaults:
25842     Roo.applyIf(meta, {
25843         totalProperty: 'total',
25844         successProperty : 'success',
25845         root : 'data',
25846         id : 'id'
25847     });
25848     
25849     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25850 };
25851 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
25852     
25853     readerType : 'Json',
25854     
25855     /**
25856      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
25857      * Used by Store query builder to append _requestMeta to params.
25858      * 
25859      */
25860     metaFromRemote : false,
25861     /**
25862      * This method is only used by a DataProxy which has retrieved data from a remote server.
25863      * @param {Object} response The XHR object which contains the JSON data in its responseText.
25864      * @return {Object} data A data block which is used by an Roo.data.Store object as
25865      * a cache of Roo.data.Records.
25866      */
25867     read : function(response){
25868         var json = response.responseText;
25869        
25870         var o = /* eval:var:o */ eval("("+json+")");
25871         if(!o) {
25872             throw {message: "JsonReader.read: Json object not found"};
25873         }
25874         
25875         if(o.metaData){
25876             
25877             delete this.ef;
25878             this.metaFromRemote = true;
25879             this.meta = o.metaData;
25880             this.recordType = Roo.data.Record.create(o.metaData.fields);
25881             this.onMetaChange(this.meta, this.recordType, o);
25882         }
25883         return this.readRecords(o);
25884     },
25885
25886     // private function a store will implement
25887     onMetaChange : function(meta, recordType, o){
25888
25889     },
25890
25891     /**
25892          * @ignore
25893          */
25894     simpleAccess: function(obj, subsc) {
25895         return obj[subsc];
25896     },
25897
25898         /**
25899          * @ignore
25900          */
25901     getJsonAccessor: function(){
25902         var re = /[\[\.]/;
25903         return function(expr) {
25904             try {
25905                 return(re.test(expr))
25906                     ? new Function("obj", "return obj." + expr)
25907                     : function(obj){
25908                         return obj[expr];
25909                     };
25910             } catch(e){}
25911             return Roo.emptyFn;
25912         };
25913     }(),
25914
25915     /**
25916      * Create a data block containing Roo.data.Records from an XML document.
25917      * @param {Object} o An object which contains an Array of row objects in the property specified
25918      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25919      * which contains the total size of the dataset.
25920      * @return {Object} data A data block which is used by an Roo.data.Store object as
25921      * a cache of Roo.data.Records.
25922      */
25923     readRecords : function(o){
25924         /**
25925          * After any data loads, the raw JSON data is available for further custom processing.
25926          * @type Object
25927          */
25928         this.o = o;
25929         var s = this.meta, Record = this.recordType,
25930             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25931
25932 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25933         if (!this.ef) {
25934             if(s.totalProperty) {
25935                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25936                 }
25937                 if(s.successProperty) {
25938                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25939                 }
25940                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25941                 if (s.id) {
25942                         var g = this.getJsonAccessor(s.id);
25943                         this.getId = function(rec) {
25944                                 var r = g(rec);  
25945                                 return (r === undefined || r === "") ? null : r;
25946                         };
25947                 } else {
25948                         this.getId = function(){return null;};
25949                 }
25950             this.ef = [];
25951             for(var jj = 0; jj < fl; jj++){
25952                 f = fi[jj];
25953                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25954                 this.ef[jj] = this.getJsonAccessor(map);
25955             }
25956         }
25957
25958         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25959         if(s.totalProperty){
25960             var vt = parseInt(this.getTotal(o), 10);
25961             if(!isNaN(vt)){
25962                 totalRecords = vt;
25963             }
25964         }
25965         if(s.successProperty){
25966             var vs = this.getSuccess(o);
25967             if(vs === false || vs === 'false'){
25968                 success = false;
25969             }
25970         }
25971         var records = [];
25972         for(var i = 0; i < c; i++){
25973             var n = root[i];
25974             var values = {};
25975             var id = this.getId(n);
25976             for(var j = 0; j < fl; j++){
25977                 f = fi[j];
25978                                 var v = this.ef[j](n);
25979                                 if (!f.convert) {
25980                                         Roo.log('missing convert for ' + f.name);
25981                                         Roo.log(f);
25982                                         continue;
25983                                 }
25984                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25985             }
25986                         if (!Record) {
25987                                 return {
25988                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
25989                                         success : false,
25990                                         records : [],
25991                                         totalRecords : 0
25992                                 };
25993                         }
25994             var record = new Record(values, id);
25995             record.json = n;
25996             records[i] = record;
25997         }
25998         return {
25999             raw : o,
26000             success : success,
26001             records : records,
26002             totalRecords : totalRecords
26003         };
26004     },
26005     // used when loading children.. @see loadDataFromChildren
26006     toLoadData: function(rec)
26007     {
26008         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26009         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26010         return { data : data, total : data.length };
26011         
26012     }
26013 });/*
26014  * Based on:
26015  * Ext JS Library 1.1.1
26016  * Copyright(c) 2006-2007, Ext JS, LLC.
26017  *
26018  * Originally Released Under LGPL - original licence link has changed is not relivant.
26019  *
26020  * Fork - LGPL
26021  * <script type="text/javascript">
26022  */
26023
26024 /**
26025  * @class Roo.data.XmlReader
26026  * @extends Roo.data.DataReader
26027  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26028  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26029  * <p>
26030  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26031  * header in the HTTP response must be set to "text/xml".</em>
26032  * <p>
26033  * Example code:
26034  * <pre><code>
26035 var RecordDef = Roo.data.Record.create([
26036    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26037    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26038 ]);
26039 var myReader = new Roo.data.XmlReader({
26040    totalRecords: "results", // The element which contains the total dataset size (optional)
26041    record: "row",           // The repeated element which contains row information
26042    id: "id"                 // The element within the row that provides an ID for the record (optional)
26043 }, RecordDef);
26044 </code></pre>
26045  * <p>
26046  * This would consume an XML file like this:
26047  * <pre><code>
26048 &lt;?xml?>
26049 &lt;dataset>
26050  &lt;results>2&lt;/results>
26051  &lt;row>
26052    &lt;id>1&lt;/id>
26053    &lt;name>Bill&lt;/name>
26054    &lt;occupation>Gardener&lt;/occupation>
26055  &lt;/row>
26056  &lt;row>
26057    &lt;id>2&lt;/id>
26058    &lt;name>Ben&lt;/name>
26059    &lt;occupation>Horticulturalist&lt;/occupation>
26060  &lt;/row>
26061 &lt;/dataset>
26062 </code></pre>
26063  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26064  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26065  * paged from the remote server.
26066  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26067  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26068  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26069  * a record identifier value.
26070  * @constructor
26071  * Create a new XmlReader
26072  * @param {Object} meta Metadata configuration options
26073  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26074  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26075  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26076  */
26077 Roo.data.XmlReader = function(meta, recordType){
26078     meta = meta || {};
26079     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26080 };
26081 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26082     
26083     readerType : 'Xml',
26084     
26085     /**
26086      * This method is only used by a DataProxy which has retrieved data from a remote server.
26087          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26088          * to contain a method called 'responseXML' that returns an XML document object.
26089      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26090      * a cache of Roo.data.Records.
26091      */
26092     read : function(response){
26093         var doc = response.responseXML;
26094         if(!doc) {
26095             throw {message: "XmlReader.read: XML Document not available"};
26096         }
26097         return this.readRecords(doc);
26098     },
26099
26100     /**
26101      * Create a data block containing Roo.data.Records from an XML document.
26102          * @param {Object} doc A parsed XML document.
26103      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26104      * a cache of Roo.data.Records.
26105      */
26106     readRecords : function(doc){
26107         /**
26108          * After any data loads/reads, the raw XML Document is available for further custom processing.
26109          * @type XMLDocument
26110          */
26111         this.xmlData = doc;
26112         var root = doc.documentElement || doc;
26113         var q = Roo.DomQuery;
26114         var recordType = this.recordType, fields = recordType.prototype.fields;
26115         var sid = this.meta.id;
26116         var totalRecords = 0, success = true;
26117         if(this.meta.totalRecords){
26118             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26119         }
26120         
26121         if(this.meta.success){
26122             var sv = q.selectValue(this.meta.success, root, true);
26123             success = sv !== false && sv !== 'false';
26124         }
26125         var records = [];
26126         var ns = q.select(this.meta.record, root);
26127         for(var i = 0, len = ns.length; i < len; i++) {
26128                 var n = ns[i];
26129                 var values = {};
26130                 var id = sid ? q.selectValue(sid, n) : undefined;
26131                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26132                     var f = fields.items[j];
26133                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26134                     v = f.convert(v);
26135                     values[f.name] = v;
26136                 }
26137                 var record = new recordType(values, id);
26138                 record.node = n;
26139                 records[records.length] = record;
26140             }
26141
26142             return {
26143                 success : success,
26144                 records : records,
26145                 totalRecords : totalRecords || records.length
26146             };
26147     }
26148 });/*
26149  * Based on:
26150  * Ext JS Library 1.1.1
26151  * Copyright(c) 2006-2007, Ext JS, LLC.
26152  *
26153  * Originally Released Under LGPL - original licence link has changed is not relivant.
26154  *
26155  * Fork - LGPL
26156  * <script type="text/javascript">
26157  */
26158
26159 /**
26160  * @class Roo.data.ArrayReader
26161  * @extends Roo.data.DataReader
26162  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26163  * Each element of that Array represents a row of data fields. The
26164  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26165  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26166  * <p>
26167  * Example code:.
26168  * <pre><code>
26169 var RecordDef = Roo.data.Record.create([
26170     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26171     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26172 ]);
26173 var myReader = new Roo.data.ArrayReader({
26174     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26175 }, RecordDef);
26176 </code></pre>
26177  * <p>
26178  * This would consume an Array like this:
26179  * <pre><code>
26180 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26181   </code></pre>
26182  
26183  * @constructor
26184  * Create a new JsonReader
26185  * @param {Object} meta Metadata configuration options.
26186  * @param {Object|Array} recordType Either an Array of field definition objects
26187  * 
26188  * @cfg {Array} fields Array of field definition objects
26189  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26190  * as specified to {@link Roo.data.Record#create},
26191  * or an {@link Roo.data.Record} object
26192  *
26193  * 
26194  * created using {@link Roo.data.Record#create}.
26195  */
26196 Roo.data.ArrayReader = function(meta, recordType)
26197 {    
26198     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26199 };
26200
26201 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26202     
26203       /**
26204      * Create a data block containing Roo.data.Records from an XML document.
26205      * @param {Object} o An Array of row objects which represents the dataset.
26206      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26207      * a cache of Roo.data.Records.
26208      */
26209     readRecords : function(o)
26210     {
26211         var sid = this.meta ? this.meta.id : null;
26212         var recordType = this.recordType, fields = recordType.prototype.fields;
26213         var records = [];
26214         var root = o;
26215         for(var i = 0; i < root.length; i++){
26216             var n = root[i];
26217             var values = {};
26218             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26219             for(var j = 0, jlen = fields.length; j < jlen; j++){
26220                 var f = fields.items[j];
26221                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26222                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26223                 v = f.convert(v);
26224                 values[f.name] = v;
26225             }
26226             var record = new recordType(values, id);
26227             record.json = n;
26228             records[records.length] = record;
26229         }
26230         return {
26231             records : records,
26232             totalRecords : records.length
26233         };
26234     },
26235     // used when loading children.. @see loadDataFromChildren
26236     toLoadData: function(rec)
26237     {
26238         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26239         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26240         
26241     }
26242     
26243     
26244 });/*
26245  * Based on:
26246  * Ext JS Library 1.1.1
26247  * Copyright(c) 2006-2007, Ext JS, LLC.
26248  *
26249  * Originally Released Under LGPL - original licence link has changed is not relivant.
26250  *
26251  * Fork - LGPL
26252  * <script type="text/javascript">
26253  */
26254
26255
26256 /**
26257  * @class Roo.data.Tree
26258  * @extends Roo.util.Observable
26259  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26260  * in the tree have most standard DOM functionality.
26261  * @constructor
26262  * @param {Node} root (optional) The root node
26263  */
26264 Roo.data.Tree = function(root){
26265    this.nodeHash = {};
26266    /**
26267     * The root node for this tree
26268     * @type Node
26269     */
26270    this.root = null;
26271    if(root){
26272        this.setRootNode(root);
26273    }
26274    this.addEvents({
26275        /**
26276         * @event append
26277         * Fires when a new child node is appended to a node in this tree.
26278         * @param {Tree} tree The owner tree
26279         * @param {Node} parent The parent node
26280         * @param {Node} node The newly appended node
26281         * @param {Number} index The index of the newly appended node
26282         */
26283        "append" : true,
26284        /**
26285         * @event remove
26286         * Fires when a child node is removed from a node in this tree.
26287         * @param {Tree} tree The owner tree
26288         * @param {Node} parent The parent node
26289         * @param {Node} node The child node removed
26290         */
26291        "remove" : true,
26292        /**
26293         * @event move
26294         * Fires when a node is moved to a new location in the tree
26295         * @param {Tree} tree The owner tree
26296         * @param {Node} node The node moved
26297         * @param {Node} oldParent The old parent of this node
26298         * @param {Node} newParent The new parent of this node
26299         * @param {Number} index The index it was moved to
26300         */
26301        "move" : true,
26302        /**
26303         * @event insert
26304         * Fires when a new child node is inserted in a node in this tree.
26305         * @param {Tree} tree The owner tree
26306         * @param {Node} parent The parent node
26307         * @param {Node} node The child node inserted
26308         * @param {Node} refNode The child node the node was inserted before
26309         */
26310        "insert" : true,
26311        /**
26312         * @event beforeappend
26313         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26314         * @param {Tree} tree The owner tree
26315         * @param {Node} parent The parent node
26316         * @param {Node} node The child node to be appended
26317         */
26318        "beforeappend" : true,
26319        /**
26320         * @event beforeremove
26321         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26322         * @param {Tree} tree The owner tree
26323         * @param {Node} parent The parent node
26324         * @param {Node} node The child node to be removed
26325         */
26326        "beforeremove" : true,
26327        /**
26328         * @event beforemove
26329         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26330         * @param {Tree} tree The owner tree
26331         * @param {Node} node The node being moved
26332         * @param {Node} oldParent The parent of the node
26333         * @param {Node} newParent The new parent the node is moving to
26334         * @param {Number} index The index it is being moved to
26335         */
26336        "beforemove" : true,
26337        /**
26338         * @event beforeinsert
26339         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26340         * @param {Tree} tree The owner tree
26341         * @param {Node} parent The parent node
26342         * @param {Node} node The child node to be inserted
26343         * @param {Node} refNode The child node the node is being inserted before
26344         */
26345        "beforeinsert" : true
26346    });
26347
26348     Roo.data.Tree.superclass.constructor.call(this);
26349 };
26350
26351 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26352     pathSeparator: "/",
26353
26354     proxyNodeEvent : function(){
26355         return this.fireEvent.apply(this, arguments);
26356     },
26357
26358     /**
26359      * Returns the root node for this tree.
26360      * @return {Node}
26361      */
26362     getRootNode : function(){
26363         return this.root;
26364     },
26365
26366     /**
26367      * Sets the root node for this tree.
26368      * @param {Node} node
26369      * @return {Node}
26370      */
26371     setRootNode : function(node){
26372         this.root = node;
26373         node.ownerTree = this;
26374         node.isRoot = true;
26375         this.registerNode(node);
26376         return node;
26377     },
26378
26379     /**
26380      * Gets a node in this tree by its id.
26381      * @param {String} id
26382      * @return {Node}
26383      */
26384     getNodeById : function(id){
26385         return this.nodeHash[id];
26386     },
26387
26388     registerNode : function(node){
26389         this.nodeHash[node.id] = node;
26390     },
26391
26392     unregisterNode : function(node){
26393         delete this.nodeHash[node.id];
26394     },
26395
26396     toString : function(){
26397         return "[Tree"+(this.id?" "+this.id:"")+"]";
26398     }
26399 });
26400
26401 /**
26402  * @class Roo.data.Node
26403  * @extends Roo.util.Observable
26404  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26405  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26406  * @constructor
26407  * @param {Object} attributes The attributes/config for the node
26408  */
26409 Roo.data.Node = function(attributes){
26410     /**
26411      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26412      * @type {Object}
26413      */
26414     this.attributes = attributes || {};
26415     this.leaf = this.attributes.leaf;
26416     /**
26417      * The node id. @type String
26418      */
26419     this.id = this.attributes.id;
26420     if(!this.id){
26421         this.id = Roo.id(null, "ynode-");
26422         this.attributes.id = this.id;
26423     }
26424      
26425     
26426     /**
26427      * All child nodes of this node. @type Array
26428      */
26429     this.childNodes = [];
26430     if(!this.childNodes.indexOf){ // indexOf is a must
26431         this.childNodes.indexOf = function(o){
26432             for(var i = 0, len = this.length; i < len; i++){
26433                 if(this[i] == o) {
26434                     return i;
26435                 }
26436             }
26437             return -1;
26438         };
26439     }
26440     /**
26441      * The parent node for this node. @type Node
26442      */
26443     this.parentNode = null;
26444     /**
26445      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26446      */
26447     this.firstChild = null;
26448     /**
26449      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26450      */
26451     this.lastChild = null;
26452     /**
26453      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26454      */
26455     this.previousSibling = null;
26456     /**
26457      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26458      */
26459     this.nextSibling = null;
26460
26461     this.addEvents({
26462        /**
26463         * @event append
26464         * Fires when a new child node is appended
26465         * @param {Tree} tree The owner tree
26466         * @param {Node} this This node
26467         * @param {Node} node The newly appended node
26468         * @param {Number} index The index of the newly appended node
26469         */
26470        "append" : true,
26471        /**
26472         * @event remove
26473         * Fires when a child node is removed
26474         * @param {Tree} tree The owner tree
26475         * @param {Node} this This node
26476         * @param {Node} node The removed node
26477         */
26478        "remove" : true,
26479        /**
26480         * @event move
26481         * Fires when this node is moved to a new location in the tree
26482         * @param {Tree} tree The owner tree
26483         * @param {Node} this This node
26484         * @param {Node} oldParent The old parent of this node
26485         * @param {Node} newParent The new parent of this node
26486         * @param {Number} index The index it was moved to
26487         */
26488        "move" : true,
26489        /**
26490         * @event insert
26491         * Fires when a new child node is inserted.
26492         * @param {Tree} tree The owner tree
26493         * @param {Node} this This node
26494         * @param {Node} node The child node inserted
26495         * @param {Node} refNode The child node the node was inserted before
26496         */
26497        "insert" : true,
26498        /**
26499         * @event beforeappend
26500         * Fires before a new child is appended, return false to cancel the append.
26501         * @param {Tree} tree The owner tree
26502         * @param {Node} this This node
26503         * @param {Node} node The child node to be appended
26504         */
26505        "beforeappend" : true,
26506        /**
26507         * @event beforeremove
26508         * Fires before a child is removed, return false to cancel the remove.
26509         * @param {Tree} tree The owner tree
26510         * @param {Node} this This node
26511         * @param {Node} node The child node to be removed
26512         */
26513        "beforeremove" : true,
26514        /**
26515         * @event beforemove
26516         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26517         * @param {Tree} tree The owner tree
26518         * @param {Node} this This node
26519         * @param {Node} oldParent The parent of this node
26520         * @param {Node} newParent The new parent this node is moving to
26521         * @param {Number} index The index it is being moved to
26522         */
26523        "beforemove" : true,
26524        /**
26525         * @event beforeinsert
26526         * Fires before a new child is inserted, return false to cancel the insert.
26527         * @param {Tree} tree The owner tree
26528         * @param {Node} this This node
26529         * @param {Node} node The child node to be inserted
26530         * @param {Node} refNode The child node the node is being inserted before
26531         */
26532        "beforeinsert" : true
26533    });
26534     this.listeners = this.attributes.listeners;
26535     Roo.data.Node.superclass.constructor.call(this);
26536 };
26537
26538 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26539     fireEvent : function(evtName){
26540         // first do standard event for this node
26541         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26542             return false;
26543         }
26544         // then bubble it up to the tree if the event wasn't cancelled
26545         var ot = this.getOwnerTree();
26546         if(ot){
26547             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26548                 return false;
26549             }
26550         }
26551         return true;
26552     },
26553
26554     /**
26555      * Returns true if this node is a leaf
26556      * @return {Boolean}
26557      */
26558     isLeaf : function(){
26559         return this.leaf === true;
26560     },
26561
26562     // private
26563     setFirstChild : function(node){
26564         this.firstChild = node;
26565     },
26566
26567     //private
26568     setLastChild : function(node){
26569         this.lastChild = node;
26570     },
26571
26572
26573     /**
26574      * Returns true if this node is the last child of its parent
26575      * @return {Boolean}
26576      */
26577     isLast : function(){
26578        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26579     },
26580
26581     /**
26582      * Returns true if this node is the first child of its parent
26583      * @return {Boolean}
26584      */
26585     isFirst : function(){
26586        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26587     },
26588
26589     hasChildNodes : function(){
26590         return !this.isLeaf() && this.childNodes.length > 0;
26591     },
26592
26593     /**
26594      * Insert node(s) as the last child node of this node.
26595      * @param {Node/Array} node The node or Array of nodes to append
26596      * @return {Node} The appended node if single append, or null if an array was passed
26597      */
26598     appendChild : function(node){
26599         var multi = false;
26600         if(node instanceof Array){
26601             multi = node;
26602         }else if(arguments.length > 1){
26603             multi = arguments;
26604         }
26605         
26606         // if passed an array or multiple args do them one by one
26607         if(multi){
26608             for(var i = 0, len = multi.length; i < len; i++) {
26609                 this.appendChild(multi[i]);
26610             }
26611         }else{
26612             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26613                 return false;
26614             }
26615             var index = this.childNodes.length;
26616             var oldParent = node.parentNode;
26617             // it's a move, make sure we move it cleanly
26618             if(oldParent){
26619                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26620                     return false;
26621                 }
26622                 oldParent.removeChild(node);
26623             }
26624             
26625             index = this.childNodes.length;
26626             if(index == 0){
26627                 this.setFirstChild(node);
26628             }
26629             this.childNodes.push(node);
26630             node.parentNode = this;
26631             var ps = this.childNodes[index-1];
26632             if(ps){
26633                 node.previousSibling = ps;
26634                 ps.nextSibling = node;
26635             }else{
26636                 node.previousSibling = null;
26637             }
26638             node.nextSibling = null;
26639             this.setLastChild(node);
26640             node.setOwnerTree(this.getOwnerTree());
26641             this.fireEvent("append", this.ownerTree, this, node, index);
26642             if(this.ownerTree) {
26643                 this.ownerTree.fireEvent("appendnode", this, node, index);
26644             }
26645             if(oldParent){
26646                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26647             }
26648             return node;
26649         }
26650     },
26651
26652     /**
26653      * Removes a child node from this node.
26654      * @param {Node} node The node to remove
26655      * @return {Node} The removed node
26656      */
26657     removeChild : function(node){
26658         var index = this.childNodes.indexOf(node);
26659         if(index == -1){
26660             return false;
26661         }
26662         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26663             return false;
26664         }
26665
26666         // remove it from childNodes collection
26667         this.childNodes.splice(index, 1);
26668
26669         // update siblings
26670         if(node.previousSibling){
26671             node.previousSibling.nextSibling = node.nextSibling;
26672         }
26673         if(node.nextSibling){
26674             node.nextSibling.previousSibling = node.previousSibling;
26675         }
26676
26677         // update child refs
26678         if(this.firstChild == node){
26679             this.setFirstChild(node.nextSibling);
26680         }
26681         if(this.lastChild == node){
26682             this.setLastChild(node.previousSibling);
26683         }
26684
26685         node.setOwnerTree(null);
26686         // clear any references from the node
26687         node.parentNode = null;
26688         node.previousSibling = null;
26689         node.nextSibling = null;
26690         this.fireEvent("remove", this.ownerTree, this, node);
26691         return node;
26692     },
26693
26694     /**
26695      * Inserts the first node before the second node in this nodes childNodes collection.
26696      * @param {Node} node The node to insert
26697      * @param {Node} refNode The node to insert before (if null the node is appended)
26698      * @return {Node} The inserted node
26699      */
26700     insertBefore : function(node, refNode){
26701         if(!refNode){ // like standard Dom, refNode can be null for append
26702             return this.appendChild(node);
26703         }
26704         // nothing to do
26705         if(node == refNode){
26706             return false;
26707         }
26708
26709         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
26710             return false;
26711         }
26712         var index = this.childNodes.indexOf(refNode);
26713         var oldParent = node.parentNode;
26714         var refIndex = index;
26715
26716         // when moving internally, indexes will change after remove
26717         if(oldParent == this && this.childNodes.indexOf(node) < index){
26718             refIndex--;
26719         }
26720
26721         // it's a move, make sure we move it cleanly
26722         if(oldParent){
26723             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
26724                 return false;
26725             }
26726             oldParent.removeChild(node);
26727         }
26728         if(refIndex == 0){
26729             this.setFirstChild(node);
26730         }
26731         this.childNodes.splice(refIndex, 0, node);
26732         node.parentNode = this;
26733         var ps = this.childNodes[refIndex-1];
26734         if(ps){
26735             node.previousSibling = ps;
26736             ps.nextSibling = node;
26737         }else{
26738             node.previousSibling = null;
26739         }
26740         node.nextSibling = refNode;
26741         refNode.previousSibling = node;
26742         node.setOwnerTree(this.getOwnerTree());
26743         this.fireEvent("insert", this.ownerTree, this, node, refNode);
26744         if(oldParent){
26745             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
26746         }
26747         return node;
26748     },
26749
26750     /**
26751      * Returns the child node at the specified index.
26752      * @param {Number} index
26753      * @return {Node}
26754      */
26755     item : function(index){
26756         return this.childNodes[index];
26757     },
26758
26759     /**
26760      * Replaces one child node in this node with another.
26761      * @param {Node} newChild The replacement node
26762      * @param {Node} oldChild The node to replace
26763      * @return {Node} The replaced node
26764      */
26765     replaceChild : function(newChild, oldChild){
26766         this.insertBefore(newChild, oldChild);
26767         this.removeChild(oldChild);
26768         return oldChild;
26769     },
26770
26771     /**
26772      * Returns the index of a child node
26773      * @param {Node} node
26774      * @return {Number} The index of the node or -1 if it was not found
26775      */
26776     indexOf : function(child){
26777         return this.childNodes.indexOf(child);
26778     },
26779
26780     /**
26781      * Returns the tree this node is in.
26782      * @return {Tree}
26783      */
26784     getOwnerTree : function(){
26785         // if it doesn't have one, look for one
26786         if(!this.ownerTree){
26787             var p = this;
26788             while(p){
26789                 if(p.ownerTree){
26790                     this.ownerTree = p.ownerTree;
26791                     break;
26792                 }
26793                 p = p.parentNode;
26794             }
26795         }
26796         return this.ownerTree;
26797     },
26798
26799     /**
26800      * Returns depth of this node (the root node has a depth of 0)
26801      * @return {Number}
26802      */
26803     getDepth : function(){
26804         var depth = 0;
26805         var p = this;
26806         while(p.parentNode){
26807             ++depth;
26808             p = p.parentNode;
26809         }
26810         return depth;
26811     },
26812
26813     // private
26814     setOwnerTree : function(tree){
26815         // if it's move, we need to update everyone
26816         if(tree != this.ownerTree){
26817             if(this.ownerTree){
26818                 this.ownerTree.unregisterNode(this);
26819             }
26820             this.ownerTree = tree;
26821             var cs = this.childNodes;
26822             for(var i = 0, len = cs.length; i < len; i++) {
26823                 cs[i].setOwnerTree(tree);
26824             }
26825             if(tree){
26826                 tree.registerNode(this);
26827             }
26828         }
26829     },
26830
26831     /**
26832      * Returns the path for this node. The path can be used to expand or select this node programmatically.
26833      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
26834      * @return {String} The path
26835      */
26836     getPath : function(attr){
26837         attr = attr || "id";
26838         var p = this.parentNode;
26839         var b = [this.attributes[attr]];
26840         while(p){
26841             b.unshift(p.attributes[attr]);
26842             p = p.parentNode;
26843         }
26844         var sep = this.getOwnerTree().pathSeparator;
26845         return sep + b.join(sep);
26846     },
26847
26848     /**
26849      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26850      * function call will be the scope provided or the current node. The arguments to the function
26851      * will be the args provided or the current node. If the function returns false at any point,
26852      * the bubble is stopped.
26853      * @param {Function} fn The function to call
26854      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26855      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26856      */
26857     bubble : function(fn, scope, args){
26858         var p = this;
26859         while(p){
26860             if(fn.call(scope || p, args || p) === false){
26861                 break;
26862             }
26863             p = p.parentNode;
26864         }
26865     },
26866
26867     /**
26868      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26869      * function call will be the scope provided or the current node. The arguments to the function
26870      * will be the args provided or the current node. If the function returns false at any point,
26871      * the cascade is stopped on that branch.
26872      * @param {Function} fn The function to call
26873      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26874      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26875      */
26876     cascade : function(fn, scope, args){
26877         if(fn.call(scope || this, args || this) !== false){
26878             var cs = this.childNodes;
26879             for(var i = 0, len = cs.length; i < len; i++) {
26880                 cs[i].cascade(fn, scope, args);
26881             }
26882         }
26883     },
26884
26885     /**
26886      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26887      * function call will be the scope provided or the current node. The arguments to the function
26888      * will be the args provided or the current node. If the function returns false at any point,
26889      * the iteration stops.
26890      * @param {Function} fn The function to call
26891      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26892      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26893      */
26894     eachChild : function(fn, scope, args){
26895         var cs = this.childNodes;
26896         for(var i = 0, len = cs.length; i < len; i++) {
26897                 if(fn.call(scope || this, args || cs[i]) === false){
26898                     break;
26899                 }
26900         }
26901     },
26902
26903     /**
26904      * Finds the first child that has the attribute with the specified value.
26905      * @param {String} attribute The attribute name
26906      * @param {Mixed} value The value to search for
26907      * @return {Node} The found child or null if none was found
26908      */
26909     findChild : function(attribute, value){
26910         var cs = this.childNodes;
26911         for(var i = 0, len = cs.length; i < len; i++) {
26912                 if(cs[i].attributes[attribute] == value){
26913                     return cs[i];
26914                 }
26915         }
26916         return null;
26917     },
26918
26919     /**
26920      * Finds the first child by a custom function. The child matches if the function passed
26921      * returns true.
26922      * @param {Function} fn
26923      * @param {Object} scope (optional)
26924      * @return {Node} The found child or null if none was found
26925      */
26926     findChildBy : function(fn, scope){
26927         var cs = this.childNodes;
26928         for(var i = 0, len = cs.length; i < len; i++) {
26929                 if(fn.call(scope||cs[i], cs[i]) === true){
26930                     return cs[i];
26931                 }
26932         }
26933         return null;
26934     },
26935
26936     /**
26937      * Sorts this nodes children using the supplied sort function
26938      * @param {Function} fn
26939      * @param {Object} scope (optional)
26940      */
26941     sort : function(fn, scope){
26942         var cs = this.childNodes;
26943         var len = cs.length;
26944         if(len > 0){
26945             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26946             cs.sort(sortFn);
26947             for(var i = 0; i < len; i++){
26948                 var n = cs[i];
26949                 n.previousSibling = cs[i-1];
26950                 n.nextSibling = cs[i+1];
26951                 if(i == 0){
26952                     this.setFirstChild(n);
26953                 }
26954                 if(i == len-1){
26955                     this.setLastChild(n);
26956                 }
26957             }
26958         }
26959     },
26960
26961     /**
26962      * Returns true if this node is an ancestor (at any point) of the passed node.
26963      * @param {Node} node
26964      * @return {Boolean}
26965      */
26966     contains : function(node){
26967         return node.isAncestor(this);
26968     },
26969
26970     /**
26971      * Returns true if the passed node is an ancestor (at any point) of this node.
26972      * @param {Node} node
26973      * @return {Boolean}
26974      */
26975     isAncestor : function(node){
26976         var p = this.parentNode;
26977         while(p){
26978             if(p == node){
26979                 return true;
26980             }
26981             p = p.parentNode;
26982         }
26983         return false;
26984     },
26985
26986     toString : function(){
26987         return "[Node"+(this.id?" "+this.id:"")+"]";
26988     }
26989 });/*
26990  * Based on:
26991  * Ext JS Library 1.1.1
26992  * Copyright(c) 2006-2007, Ext JS, LLC.
26993  *
26994  * Originally Released Under LGPL - original licence link has changed is not relivant.
26995  *
26996  * Fork - LGPL
26997  * <script type="text/javascript">
26998  */
26999
27000
27001 /**
27002  * @class Roo.Shadow
27003  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
27004  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
27005  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27006  * @constructor
27007  * Create a new Shadow
27008  * @param {Object} config The config object
27009  */
27010 Roo.Shadow = function(config){
27011     Roo.apply(this, config);
27012     if(typeof this.mode != "string"){
27013         this.mode = this.defaultMode;
27014     }
27015     var o = this.offset, a = {h: 0};
27016     var rad = Math.floor(this.offset/2);
27017     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27018         case "drop":
27019             a.w = 0;
27020             a.l = a.t = o;
27021             a.t -= 1;
27022             if(Roo.isIE){
27023                 a.l -= this.offset + rad;
27024                 a.t -= this.offset + rad;
27025                 a.w -= rad;
27026                 a.h -= rad;
27027                 a.t += 1;
27028             }
27029         break;
27030         case "sides":
27031             a.w = (o*2);
27032             a.l = -o;
27033             a.t = o-1;
27034             if(Roo.isIE){
27035                 a.l -= (this.offset - rad);
27036                 a.t -= this.offset + rad;
27037                 a.l += 1;
27038                 a.w -= (this.offset - rad)*2;
27039                 a.w -= rad + 1;
27040                 a.h -= 1;
27041             }
27042         break;
27043         case "frame":
27044             a.w = a.h = (o*2);
27045             a.l = a.t = -o;
27046             a.t += 1;
27047             a.h -= 2;
27048             if(Roo.isIE){
27049                 a.l -= (this.offset - rad);
27050                 a.t -= (this.offset - rad);
27051                 a.l += 1;
27052                 a.w -= (this.offset + rad + 1);
27053                 a.h -= (this.offset + rad);
27054                 a.h += 1;
27055             }
27056         break;
27057     };
27058
27059     this.adjusts = a;
27060 };
27061
27062 Roo.Shadow.prototype = {
27063     /**
27064      * @cfg {String} mode
27065      * The shadow display mode.  Supports the following options:<br />
27066      * sides: Shadow displays on both sides and bottom only<br />
27067      * frame: Shadow displays equally on all four sides<br />
27068      * drop: Traditional bottom-right drop shadow (default)
27069      */
27070     mode: false,
27071     /**
27072      * @cfg {String} offset
27073      * The number of pixels to offset the shadow from the element (defaults to 4)
27074      */
27075     offset: 4,
27076
27077     // private
27078     defaultMode: "drop",
27079
27080     /**
27081      * Displays the shadow under the target element
27082      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27083      */
27084     show : function(target){
27085         target = Roo.get(target);
27086         if(!this.el){
27087             this.el = Roo.Shadow.Pool.pull();
27088             if(this.el.dom.nextSibling != target.dom){
27089                 this.el.insertBefore(target);
27090             }
27091         }
27092         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27093         if(Roo.isIE){
27094             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27095         }
27096         this.realign(
27097             target.getLeft(true),
27098             target.getTop(true),
27099             target.getWidth(),
27100             target.getHeight()
27101         );
27102         this.el.dom.style.display = "block";
27103     },
27104
27105     /**
27106      * Returns true if the shadow is visible, else false
27107      */
27108     isVisible : function(){
27109         return this.el ? true : false;  
27110     },
27111
27112     /**
27113      * Direct alignment when values are already available. Show must be called at least once before
27114      * calling this method to ensure it is initialized.
27115      * @param {Number} left The target element left position
27116      * @param {Number} top The target element top position
27117      * @param {Number} width The target element width
27118      * @param {Number} height The target element height
27119      */
27120     realign : function(l, t, w, h){
27121         if(!this.el){
27122             return;
27123         }
27124         var a = this.adjusts, d = this.el.dom, s = d.style;
27125         var iea = 0;
27126         s.left = (l+a.l)+"px";
27127         s.top = (t+a.t)+"px";
27128         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27129  
27130         if(s.width != sws || s.height != shs){
27131             s.width = sws;
27132             s.height = shs;
27133             if(!Roo.isIE){
27134                 var cn = d.childNodes;
27135                 var sww = Math.max(0, (sw-12))+"px";
27136                 cn[0].childNodes[1].style.width = sww;
27137                 cn[1].childNodes[1].style.width = sww;
27138                 cn[2].childNodes[1].style.width = sww;
27139                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27140             }
27141         }
27142     },
27143
27144     /**
27145      * Hides this shadow
27146      */
27147     hide : function(){
27148         if(this.el){
27149             this.el.dom.style.display = "none";
27150             Roo.Shadow.Pool.push(this.el);
27151             delete this.el;
27152         }
27153     },
27154
27155     /**
27156      * Adjust the z-index of this shadow
27157      * @param {Number} zindex The new z-index
27158      */
27159     setZIndex : function(z){
27160         this.zIndex = z;
27161         if(this.el){
27162             this.el.setStyle("z-index", z);
27163         }
27164     }
27165 };
27166
27167 // Private utility class that manages the internal Shadow cache
27168 Roo.Shadow.Pool = function(){
27169     var p = [];
27170     var markup = Roo.isIE ?
27171                  '<div class="x-ie-shadow"></div>' :
27172                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
27173     return {
27174         pull : function(){
27175             var sh = p.shift();
27176             if(!sh){
27177                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27178                 sh.autoBoxAdjust = false;
27179             }
27180             return sh;
27181         },
27182
27183         push : function(sh){
27184             p.push(sh);
27185         }
27186     };
27187 }();/*
27188  * Based on:
27189  * Ext JS Library 1.1.1
27190  * Copyright(c) 2006-2007, Ext JS, LLC.
27191  *
27192  * Originally Released Under LGPL - original licence link has changed is not relivant.
27193  *
27194  * Fork - LGPL
27195  * <script type="text/javascript">
27196  */
27197
27198
27199 /**
27200  * @class Roo.SplitBar
27201  * @extends Roo.util.Observable
27202  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27203  * <br><br>
27204  * Usage:
27205  * <pre><code>
27206 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27207                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27208 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27209 split.minSize = 100;
27210 split.maxSize = 600;
27211 split.animate = true;
27212 split.on('moved', splitterMoved);
27213 </code></pre>
27214  * @constructor
27215  * Create a new SplitBar
27216  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27217  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27218  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27219  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27220                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27221                         position of the SplitBar).
27222  */
27223 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27224     
27225     /** @private */
27226     this.el = Roo.get(dragElement, true);
27227     this.el.dom.unselectable = "on";
27228     /** @private */
27229     this.resizingEl = Roo.get(resizingElement, true);
27230
27231     /**
27232      * @private
27233      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27234      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27235      * @type Number
27236      */
27237     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27238     
27239     /**
27240      * The minimum size of the resizing element. (Defaults to 0)
27241      * @type Number
27242      */
27243     this.minSize = 0;
27244     
27245     /**
27246      * The maximum size of the resizing element. (Defaults to 2000)
27247      * @type Number
27248      */
27249     this.maxSize = 2000;
27250     
27251     /**
27252      * Whether to animate the transition to the new size
27253      * @type Boolean
27254      */
27255     this.animate = false;
27256     
27257     /**
27258      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27259      * @type Boolean
27260      */
27261     this.useShim = false;
27262     
27263     /** @private */
27264     this.shim = null;
27265     
27266     if(!existingProxy){
27267         /** @private */
27268         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27269     }else{
27270         this.proxy = Roo.get(existingProxy).dom;
27271     }
27272     /** @private */
27273     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27274     
27275     /** @private */
27276     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27277     
27278     /** @private */
27279     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27280     
27281     /** @private */
27282     this.dragSpecs = {};
27283     
27284     /**
27285      * @private The adapter to use to positon and resize elements
27286      */
27287     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27288     this.adapter.init(this);
27289     
27290     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27291         /** @private */
27292         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27293         this.el.addClass("x-splitbar-h");
27294     }else{
27295         /** @private */
27296         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27297         this.el.addClass("x-splitbar-v");
27298     }
27299     
27300     this.addEvents({
27301         /**
27302          * @event resize
27303          * Fires when the splitter is moved (alias for {@link #event-moved})
27304          * @param {Roo.SplitBar} this
27305          * @param {Number} newSize the new width or height
27306          */
27307         "resize" : true,
27308         /**
27309          * @event moved
27310          * Fires when the splitter is moved
27311          * @param {Roo.SplitBar} this
27312          * @param {Number} newSize the new width or height
27313          */
27314         "moved" : true,
27315         /**
27316          * @event beforeresize
27317          * Fires before the splitter is dragged
27318          * @param {Roo.SplitBar} this
27319          */
27320         "beforeresize" : true,
27321
27322         "beforeapply" : true
27323     });
27324
27325     Roo.util.Observable.call(this);
27326 };
27327
27328 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27329     onStartProxyDrag : function(x, y){
27330         this.fireEvent("beforeresize", this);
27331         if(!this.overlay){
27332             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27333             o.unselectable();
27334             o.enableDisplayMode("block");
27335             // all splitbars share the same overlay
27336             Roo.SplitBar.prototype.overlay = o;
27337         }
27338         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27339         this.overlay.show();
27340         Roo.get(this.proxy).setDisplayed("block");
27341         var size = this.adapter.getElementSize(this);
27342         this.activeMinSize = this.getMinimumSize();;
27343         this.activeMaxSize = this.getMaximumSize();;
27344         var c1 = size - this.activeMinSize;
27345         var c2 = Math.max(this.activeMaxSize - size, 0);
27346         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27347             this.dd.resetConstraints();
27348             this.dd.setXConstraint(
27349                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27350                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27351             );
27352             this.dd.setYConstraint(0, 0);
27353         }else{
27354             this.dd.resetConstraints();
27355             this.dd.setXConstraint(0, 0);
27356             this.dd.setYConstraint(
27357                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27358                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27359             );
27360          }
27361         this.dragSpecs.startSize = size;
27362         this.dragSpecs.startPoint = [x, y];
27363         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27364     },
27365     
27366     /** 
27367      * @private Called after the drag operation by the DDProxy
27368      */
27369     onEndProxyDrag : function(e){
27370         Roo.get(this.proxy).setDisplayed(false);
27371         var endPoint = Roo.lib.Event.getXY(e);
27372         if(this.overlay){
27373             this.overlay.hide();
27374         }
27375         var newSize;
27376         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27377             newSize = this.dragSpecs.startSize + 
27378                 (this.placement == Roo.SplitBar.LEFT ?
27379                     endPoint[0] - this.dragSpecs.startPoint[0] :
27380                     this.dragSpecs.startPoint[0] - endPoint[0]
27381                 );
27382         }else{
27383             newSize = this.dragSpecs.startSize + 
27384                 (this.placement == Roo.SplitBar.TOP ?
27385                     endPoint[1] - this.dragSpecs.startPoint[1] :
27386                     this.dragSpecs.startPoint[1] - endPoint[1]
27387                 );
27388         }
27389         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27390         if(newSize != this.dragSpecs.startSize){
27391             if(this.fireEvent('beforeapply', this, newSize) !== false){
27392                 this.adapter.setElementSize(this, newSize);
27393                 this.fireEvent("moved", this, newSize);
27394                 this.fireEvent("resize", this, newSize);
27395             }
27396         }
27397     },
27398     
27399     /**
27400      * Get the adapter this SplitBar uses
27401      * @return The adapter object
27402      */
27403     getAdapter : function(){
27404         return this.adapter;
27405     },
27406     
27407     /**
27408      * Set the adapter this SplitBar uses
27409      * @param {Object} adapter A SplitBar adapter object
27410      */
27411     setAdapter : function(adapter){
27412         this.adapter = adapter;
27413         this.adapter.init(this);
27414     },
27415     
27416     /**
27417      * Gets the minimum size for the resizing element
27418      * @return {Number} The minimum size
27419      */
27420     getMinimumSize : function(){
27421         return this.minSize;
27422     },
27423     
27424     /**
27425      * Sets the minimum size for the resizing element
27426      * @param {Number} minSize The minimum size
27427      */
27428     setMinimumSize : function(minSize){
27429         this.minSize = minSize;
27430     },
27431     
27432     /**
27433      * Gets the maximum size for the resizing element
27434      * @return {Number} The maximum size
27435      */
27436     getMaximumSize : function(){
27437         return this.maxSize;
27438     },
27439     
27440     /**
27441      * Sets the maximum size for the resizing element
27442      * @param {Number} maxSize The maximum size
27443      */
27444     setMaximumSize : function(maxSize){
27445         this.maxSize = maxSize;
27446     },
27447     
27448     /**
27449      * Sets the initialize size for the resizing element
27450      * @param {Number} size The initial size
27451      */
27452     setCurrentSize : function(size){
27453         var oldAnimate = this.animate;
27454         this.animate = false;
27455         this.adapter.setElementSize(this, size);
27456         this.animate = oldAnimate;
27457     },
27458     
27459     /**
27460      * Destroy this splitbar. 
27461      * @param {Boolean} removeEl True to remove the element
27462      */
27463     destroy : function(removeEl){
27464         if(this.shim){
27465             this.shim.remove();
27466         }
27467         this.dd.unreg();
27468         this.proxy.parentNode.removeChild(this.proxy);
27469         if(removeEl){
27470             this.el.remove();
27471         }
27472     }
27473 });
27474
27475 /**
27476  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
27477  */
27478 Roo.SplitBar.createProxy = function(dir){
27479     var proxy = new Roo.Element(document.createElement("div"));
27480     proxy.unselectable();
27481     var cls = 'x-splitbar-proxy';
27482     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27483     document.body.appendChild(proxy.dom);
27484     return proxy.dom;
27485 };
27486
27487 /** 
27488  * @class Roo.SplitBar.BasicLayoutAdapter
27489  * Default Adapter. It assumes the splitter and resizing element are not positioned
27490  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27491  */
27492 Roo.SplitBar.BasicLayoutAdapter = function(){
27493 };
27494
27495 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27496     // do nothing for now
27497     init : function(s){
27498     
27499     },
27500     /**
27501      * Called before drag operations to get the current size of the resizing element. 
27502      * @param {Roo.SplitBar} s The SplitBar using this adapter
27503      */
27504      getElementSize : function(s){
27505         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27506             return s.resizingEl.getWidth();
27507         }else{
27508             return s.resizingEl.getHeight();
27509         }
27510     },
27511     
27512     /**
27513      * Called after drag operations to set the size of the resizing element.
27514      * @param {Roo.SplitBar} s The SplitBar using this adapter
27515      * @param {Number} newSize The new size to set
27516      * @param {Function} onComplete A function to be invoked when resizing is complete
27517      */
27518     setElementSize : function(s, newSize, onComplete){
27519         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27520             if(!s.animate){
27521                 s.resizingEl.setWidth(newSize);
27522                 if(onComplete){
27523                     onComplete(s, newSize);
27524                 }
27525             }else{
27526                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27527             }
27528         }else{
27529             
27530             if(!s.animate){
27531                 s.resizingEl.setHeight(newSize);
27532                 if(onComplete){
27533                     onComplete(s, newSize);
27534                 }
27535             }else{
27536                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27537             }
27538         }
27539     }
27540 };
27541
27542 /** 
27543  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27544  * @extends Roo.SplitBar.BasicLayoutAdapter
27545  * Adapter that  moves the splitter element to align with the resized sizing element. 
27546  * Used with an absolute positioned SplitBar.
27547  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27548  * document.body, make sure you assign an id to the body element.
27549  */
27550 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27551     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27552     this.container = Roo.get(container);
27553 };
27554
27555 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27556     init : function(s){
27557         this.basic.init(s);
27558     },
27559     
27560     getElementSize : function(s){
27561         return this.basic.getElementSize(s);
27562     },
27563     
27564     setElementSize : function(s, newSize, onComplete){
27565         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27566     },
27567     
27568     moveSplitter : function(s){
27569         var yes = Roo.SplitBar;
27570         switch(s.placement){
27571             case yes.LEFT:
27572                 s.el.setX(s.resizingEl.getRight());
27573                 break;
27574             case yes.RIGHT:
27575                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27576                 break;
27577             case yes.TOP:
27578                 s.el.setY(s.resizingEl.getBottom());
27579                 break;
27580             case yes.BOTTOM:
27581                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27582                 break;
27583         }
27584     }
27585 };
27586
27587 /**
27588  * Orientation constant - Create a vertical SplitBar
27589  * @static
27590  * @type Number
27591  */
27592 Roo.SplitBar.VERTICAL = 1;
27593
27594 /**
27595  * Orientation constant - Create a horizontal SplitBar
27596  * @static
27597  * @type Number
27598  */
27599 Roo.SplitBar.HORIZONTAL = 2;
27600
27601 /**
27602  * Placement constant - The resizing element is to the left of the splitter element
27603  * @static
27604  * @type Number
27605  */
27606 Roo.SplitBar.LEFT = 1;
27607
27608 /**
27609  * Placement constant - The resizing element is to the right of the splitter element
27610  * @static
27611  * @type Number
27612  */
27613 Roo.SplitBar.RIGHT = 2;
27614
27615 /**
27616  * Placement constant - The resizing element is positioned above the splitter element
27617  * @static
27618  * @type Number
27619  */
27620 Roo.SplitBar.TOP = 3;
27621
27622 /**
27623  * Placement constant - The resizing element is positioned under splitter element
27624  * @static
27625  * @type Number
27626  */
27627 Roo.SplitBar.BOTTOM = 4;
27628 /*
27629  * Based on:
27630  * Ext JS Library 1.1.1
27631  * Copyright(c) 2006-2007, Ext JS, LLC.
27632  *
27633  * Originally Released Under LGPL - original licence link has changed is not relivant.
27634  *
27635  * Fork - LGPL
27636  * <script type="text/javascript">
27637  */
27638
27639 /**
27640  * @class Roo.View
27641  * @extends Roo.util.Observable
27642  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27643  * This class also supports single and multi selection modes. <br>
27644  * Create a data model bound view:
27645  <pre><code>
27646  var store = new Roo.data.Store(...);
27647
27648  var view = new Roo.View({
27649     el : "my-element",
27650     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27651  
27652     singleSelect: true,
27653     selectedClass: "ydataview-selected",
27654     store: store
27655  });
27656
27657  // listen for node click?
27658  view.on("click", function(vw, index, node, e){
27659  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27660  });
27661
27662  // load XML data
27663  dataModel.load("foobar.xml");
27664  </code></pre>
27665  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27666  * <br><br>
27667  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27668  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27669  * 
27670  * Note: old style constructor is still suported (container, template, config)
27671  * 
27672  * @constructor
27673  * Create a new View
27674  * @param {Object} config The config object
27675  * 
27676  */
27677 Roo.View = function(config, depreciated_tpl, depreciated_config){
27678     
27679     this.parent = false;
27680     
27681     if (typeof(depreciated_tpl) == 'undefined') {
27682         // new way.. - universal constructor.
27683         Roo.apply(this, config);
27684         this.el  = Roo.get(this.el);
27685     } else {
27686         // old format..
27687         this.el  = Roo.get(config);
27688         this.tpl = depreciated_tpl;
27689         Roo.apply(this, depreciated_config);
27690     }
27691     this.wrapEl  = this.el.wrap().wrap();
27692     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
27693     
27694     
27695     if(typeof(this.tpl) == "string"){
27696         this.tpl = new Roo.Template(this.tpl);
27697     } else {
27698         // support xtype ctors..
27699         this.tpl = new Roo.factory(this.tpl, Roo);
27700     }
27701     
27702     
27703     this.tpl.compile();
27704     
27705     /** @private */
27706     this.addEvents({
27707         /**
27708          * @event beforeclick
27709          * Fires before a click is processed. Returns false to cancel the default action.
27710          * @param {Roo.View} this
27711          * @param {Number} index The index of the target node
27712          * @param {HTMLElement} node The target node
27713          * @param {Roo.EventObject} e The raw event object
27714          */
27715             "beforeclick" : true,
27716         /**
27717          * @event click
27718          * Fires when a template node is clicked.
27719          * @param {Roo.View} this
27720          * @param {Number} index The index of the target node
27721          * @param {HTMLElement} node The target node
27722          * @param {Roo.EventObject} e The raw event object
27723          */
27724             "click" : true,
27725         /**
27726          * @event dblclick
27727          * Fires when a template node is double clicked.
27728          * @param {Roo.View} this
27729          * @param {Number} index The index of the target node
27730          * @param {HTMLElement} node The target node
27731          * @param {Roo.EventObject} e The raw event object
27732          */
27733             "dblclick" : true,
27734         /**
27735          * @event contextmenu
27736          * Fires when a template node is right clicked.
27737          * @param {Roo.View} this
27738          * @param {Number} index The index of the target node
27739          * @param {HTMLElement} node The target node
27740          * @param {Roo.EventObject} e The raw event object
27741          */
27742             "contextmenu" : true,
27743         /**
27744          * @event selectionchange
27745          * Fires when the selected nodes change.
27746          * @param {Roo.View} this
27747          * @param {Array} selections Array of the selected nodes
27748          */
27749             "selectionchange" : true,
27750     
27751         /**
27752          * @event beforeselect
27753          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
27754          * @param {Roo.View} this
27755          * @param {HTMLElement} node The node to be selected
27756          * @param {Array} selections Array of currently selected nodes
27757          */
27758             "beforeselect" : true,
27759         /**
27760          * @event preparedata
27761          * Fires on every row to render, to allow you to change the data.
27762          * @param {Roo.View} this
27763          * @param {Object} data to be rendered (change this)
27764          */
27765           "preparedata" : true
27766           
27767           
27768         });
27769
27770
27771
27772     this.el.on({
27773         "click": this.onClick,
27774         "dblclick": this.onDblClick,
27775         "contextmenu": this.onContextMenu,
27776         scope:this
27777     });
27778
27779     this.selections = [];
27780     this.nodes = [];
27781     this.cmp = new Roo.CompositeElementLite([]);
27782     if(this.store){
27783         this.store = Roo.factory(this.store, Roo.data);
27784         this.setStore(this.store, true);
27785     }
27786     
27787     if ( this.footer && this.footer.xtype) {
27788            
27789          var fctr = this.wrapEl.appendChild(document.createElement("div"));
27790         
27791         this.footer.dataSource = this.store;
27792         this.footer.container = fctr;
27793         this.footer = Roo.factory(this.footer, Roo);
27794         fctr.insertFirst(this.el);
27795         
27796         // this is a bit insane - as the paging toolbar seems to detach the el..
27797 //        dom.parentNode.parentNode.parentNode
27798          // they get detached?
27799     }
27800     
27801     
27802     Roo.View.superclass.constructor.call(this);
27803     
27804     
27805 };
27806
27807 Roo.extend(Roo.View, Roo.util.Observable, {
27808     
27809      /**
27810      * @cfg {Roo.data.Store} store Data store to load data from.
27811      */
27812     store : false,
27813     
27814     /**
27815      * @cfg {String|Roo.Element} el The container element.
27816      */
27817     el : '',
27818     
27819     /**
27820      * @cfg {String|Roo.Template} tpl The template used by this View 
27821      */
27822     tpl : false,
27823     /**
27824      * @cfg {String} dataName the named area of the template to use as the data area
27825      *                          Works with domtemplates roo-name="name"
27826      */
27827     dataName: false,
27828     /**
27829      * @cfg {String} selectedClass The css class to add to selected nodes
27830      */
27831     selectedClass : "x-view-selected",
27832      /**
27833      * @cfg {String} emptyText The empty text to show when nothing is loaded.
27834      */
27835     emptyText : "",
27836     
27837     /**
27838      * @cfg {String} text to display on mask (default Loading)
27839      */
27840     mask : false,
27841     /**
27842      * @cfg {Boolean} multiSelect Allow multiple selection
27843      */
27844     multiSelect : false,
27845     /**
27846      * @cfg {Boolean} singleSelect Allow single selection
27847      */
27848     singleSelect:  false,
27849     
27850     /**
27851      * @cfg {Boolean} toggleSelect - selecting 
27852      */
27853     toggleSelect : false,
27854     
27855     /**
27856      * @cfg {Boolean} tickable - selecting 
27857      */
27858     tickable : false,
27859     
27860     /**
27861      * Returns the element this view is bound to.
27862      * @return {Roo.Element}
27863      */
27864     getEl : function(){
27865         return this.wrapEl;
27866     },
27867     
27868     
27869
27870     /**
27871      * Refreshes the view. - called by datachanged on the store. - do not call directly.
27872      */
27873     refresh : function(){
27874         //Roo.log('refresh');
27875         var t = this.tpl;
27876         
27877         // if we are using something like 'domtemplate', then
27878         // the what gets used is:
27879         // t.applySubtemplate(NAME, data, wrapping data..)
27880         // the outer template then get' applied with
27881         //     the store 'extra data'
27882         // and the body get's added to the
27883         //      roo-name="data" node?
27884         //      <span class='roo-tpl-{name}'></span> ?????
27885         
27886         
27887         
27888         this.clearSelections();
27889         this.el.update("");
27890         var html = [];
27891         var records = this.store.getRange();
27892         if(records.length < 1) {
27893             
27894             // is this valid??  = should it render a template??
27895             
27896             this.el.update(this.emptyText);
27897             return;
27898         }
27899         var el = this.el;
27900         if (this.dataName) {
27901             this.el.update(t.apply(this.store.meta)); //????
27902             el = this.el.child('.roo-tpl-' + this.dataName);
27903         }
27904         
27905         for(var i = 0, len = records.length; i < len; i++){
27906             var data = this.prepareData(records[i].data, i, records[i]);
27907             this.fireEvent("preparedata", this, data, i, records[i]);
27908             
27909             var d = Roo.apply({}, data);
27910             
27911             if(this.tickable){
27912                 Roo.apply(d, {'roo-id' : Roo.id()});
27913                 
27914                 var _this = this;
27915             
27916                 Roo.each(this.parent.item, function(item){
27917                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27918                         return;
27919                     }
27920                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27921                 });
27922             }
27923             
27924             html[html.length] = Roo.util.Format.trim(
27925                 this.dataName ?
27926                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27927                     t.apply(d)
27928             );
27929         }
27930         
27931         
27932         
27933         el.update(html.join(""));
27934         this.nodes = el.dom.childNodes;
27935         this.updateIndexes(0);
27936     },
27937     
27938
27939     /**
27940      * Function to override to reformat the data that is sent to
27941      * the template for each node.
27942      * DEPRICATED - use the preparedata event handler.
27943      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27944      * a JSON object for an UpdateManager bound view).
27945      */
27946     prepareData : function(data, index, record)
27947     {
27948         this.fireEvent("preparedata", this, data, index, record);
27949         return data;
27950     },
27951
27952     onUpdate : function(ds, record){
27953         // Roo.log('on update');   
27954         this.clearSelections();
27955         var index = this.store.indexOf(record);
27956         var n = this.nodes[index];
27957         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27958         n.parentNode.removeChild(n);
27959         this.updateIndexes(index, index);
27960     },
27961
27962     
27963     
27964 // --------- FIXME     
27965     onAdd : function(ds, records, index)
27966     {
27967         //Roo.log(['on Add', ds, records, index] );        
27968         this.clearSelections();
27969         if(this.nodes.length == 0){
27970             this.refresh();
27971             return;
27972         }
27973         var n = this.nodes[index];
27974         for(var i = 0, len = records.length; i < len; i++){
27975             var d = this.prepareData(records[i].data, i, records[i]);
27976             if(n){
27977                 this.tpl.insertBefore(n, d);
27978             }else{
27979                 
27980                 this.tpl.append(this.el, d);
27981             }
27982         }
27983         this.updateIndexes(index);
27984     },
27985
27986     onRemove : function(ds, record, index){
27987        // Roo.log('onRemove');
27988         this.clearSelections();
27989         var el = this.dataName  ?
27990             this.el.child('.roo-tpl-' + this.dataName) :
27991             this.el; 
27992         
27993         el.dom.removeChild(this.nodes[index]);
27994         this.updateIndexes(index);
27995     },
27996
27997     /**
27998      * Refresh an individual node.
27999      * @param {Number} index
28000      */
28001     refreshNode : function(index){
28002         this.onUpdate(this.store, this.store.getAt(index));
28003     },
28004
28005     updateIndexes : function(startIndex, endIndex){
28006         var ns = this.nodes;
28007         startIndex = startIndex || 0;
28008         endIndex = endIndex || ns.length - 1;
28009         for(var i = startIndex; i <= endIndex; i++){
28010             ns[i].nodeIndex = i;
28011         }
28012     },
28013
28014     /**
28015      * Changes the data store this view uses and refresh the view.
28016      * @param {Store} store
28017      */
28018     setStore : function(store, initial){
28019         if(!initial && this.store){
28020             this.store.un("datachanged", this.refresh);
28021             this.store.un("add", this.onAdd);
28022             this.store.un("remove", this.onRemove);
28023             this.store.un("update", this.onUpdate);
28024             this.store.un("clear", this.refresh);
28025             this.store.un("beforeload", this.onBeforeLoad);
28026             this.store.un("load", this.onLoad);
28027             this.store.un("loadexception", this.onLoad);
28028         }
28029         if(store){
28030           
28031             store.on("datachanged", this.refresh, this);
28032             store.on("add", this.onAdd, this);
28033             store.on("remove", this.onRemove, this);
28034             store.on("update", this.onUpdate, this);
28035             store.on("clear", this.refresh, this);
28036             store.on("beforeload", this.onBeforeLoad, this);
28037             store.on("load", this.onLoad, this);
28038             store.on("loadexception", this.onLoad, this);
28039         }
28040         
28041         if(store){
28042             this.refresh();
28043         }
28044     },
28045     /**
28046      * onbeforeLoad - masks the loading area.
28047      *
28048      */
28049     onBeforeLoad : function(store,opts)
28050     {
28051          //Roo.log('onBeforeLoad');   
28052         if (!opts.add) {
28053             this.el.update("");
28054         }
28055         this.el.mask(this.mask ? this.mask : "Loading" ); 
28056     },
28057     onLoad : function ()
28058     {
28059         this.el.unmask();
28060     },
28061     
28062
28063     /**
28064      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28065      * @param {HTMLElement} node
28066      * @return {HTMLElement} The template node
28067      */
28068     findItemFromChild : function(node){
28069         var el = this.dataName  ?
28070             this.el.child('.roo-tpl-' + this.dataName,true) :
28071             this.el.dom; 
28072         
28073         if(!node || node.parentNode == el){
28074                     return node;
28075             }
28076             var p = node.parentNode;
28077             while(p && p != el){
28078             if(p.parentNode == el){
28079                 return p;
28080             }
28081             p = p.parentNode;
28082         }
28083             return null;
28084     },
28085
28086     /** @ignore */
28087     onClick : function(e){
28088         var item = this.findItemFromChild(e.getTarget());
28089         if(item){
28090             var index = this.indexOf(item);
28091             if(this.onItemClick(item, index, e) !== false){
28092                 this.fireEvent("click", this, index, item, e);
28093             }
28094         }else{
28095             this.clearSelections();
28096         }
28097     },
28098
28099     /** @ignore */
28100     onContextMenu : function(e){
28101         var item = this.findItemFromChild(e.getTarget());
28102         if(item){
28103             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28104         }
28105     },
28106
28107     /** @ignore */
28108     onDblClick : function(e){
28109         var item = this.findItemFromChild(e.getTarget());
28110         if(item){
28111             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28112         }
28113     },
28114
28115     onItemClick : function(item, index, e)
28116     {
28117         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28118             return false;
28119         }
28120         if (this.toggleSelect) {
28121             var m = this.isSelected(item) ? 'unselect' : 'select';
28122             //Roo.log(m);
28123             var _t = this;
28124             _t[m](item, true, false);
28125             return true;
28126         }
28127         if(this.multiSelect || this.singleSelect){
28128             if(this.multiSelect && e.shiftKey && this.lastSelection){
28129                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28130             }else{
28131                 this.select(item, this.multiSelect && e.ctrlKey);
28132                 this.lastSelection = item;
28133             }
28134             
28135             if(!this.tickable){
28136                 e.preventDefault();
28137             }
28138             
28139         }
28140         return true;
28141     },
28142
28143     /**
28144      * Get the number of selected nodes.
28145      * @return {Number}
28146      */
28147     getSelectionCount : function(){
28148         return this.selections.length;
28149     },
28150
28151     /**
28152      * Get the currently selected nodes.
28153      * @return {Array} An array of HTMLElements
28154      */
28155     getSelectedNodes : function(){
28156         return this.selections;
28157     },
28158
28159     /**
28160      * Get the indexes of the selected nodes.
28161      * @return {Array}
28162      */
28163     getSelectedIndexes : function(){
28164         var indexes = [], s = this.selections;
28165         for(var i = 0, len = s.length; i < len; i++){
28166             indexes.push(s[i].nodeIndex);
28167         }
28168         return indexes;
28169     },
28170
28171     /**
28172      * Clear all selections
28173      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28174      */
28175     clearSelections : function(suppressEvent){
28176         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28177             this.cmp.elements = this.selections;
28178             this.cmp.removeClass(this.selectedClass);
28179             this.selections = [];
28180             if(!suppressEvent){
28181                 this.fireEvent("selectionchange", this, this.selections);
28182             }
28183         }
28184     },
28185
28186     /**
28187      * Returns true if the passed node is selected
28188      * @param {HTMLElement/Number} node The node or node index
28189      * @return {Boolean}
28190      */
28191     isSelected : function(node){
28192         var s = this.selections;
28193         if(s.length < 1){
28194             return false;
28195         }
28196         node = this.getNode(node);
28197         return s.indexOf(node) !== -1;
28198     },
28199
28200     /**
28201      * Selects nodes.
28202      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
28203      * @param {Boolean} keepExisting (optional) true to keep existing selections
28204      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28205      */
28206     select : function(nodeInfo, keepExisting, suppressEvent){
28207         if(nodeInfo instanceof Array){
28208             if(!keepExisting){
28209                 this.clearSelections(true);
28210             }
28211             for(var i = 0, len = nodeInfo.length; i < len; i++){
28212                 this.select(nodeInfo[i], true, true);
28213             }
28214             return;
28215         } 
28216         var node = this.getNode(nodeInfo);
28217         if(!node || this.isSelected(node)){
28218             return; // already selected.
28219         }
28220         if(!keepExisting){
28221             this.clearSelections(true);
28222         }
28223         
28224         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28225             Roo.fly(node).addClass(this.selectedClass);
28226             this.selections.push(node);
28227             if(!suppressEvent){
28228                 this.fireEvent("selectionchange", this, this.selections);
28229             }
28230         }
28231         
28232         
28233     },
28234       /**
28235      * Unselects nodes.
28236      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
28237      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28238      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28239      */
28240     unselect : function(nodeInfo, keepExisting, suppressEvent)
28241     {
28242         if(nodeInfo instanceof Array){
28243             Roo.each(this.selections, function(s) {
28244                 this.unselect(s, nodeInfo);
28245             }, this);
28246             return;
28247         }
28248         var node = this.getNode(nodeInfo);
28249         if(!node || !this.isSelected(node)){
28250             //Roo.log("not selected");
28251             return; // not selected.
28252         }
28253         // fireevent???
28254         var ns = [];
28255         Roo.each(this.selections, function(s) {
28256             if (s == node ) {
28257                 Roo.fly(node).removeClass(this.selectedClass);
28258
28259                 return;
28260             }
28261             ns.push(s);
28262         },this);
28263         
28264         this.selections= ns;
28265         this.fireEvent("selectionchange", this, this.selections);
28266     },
28267
28268     /**
28269      * Gets a template node.
28270      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28271      * @return {HTMLElement} The node or null if it wasn't found
28272      */
28273     getNode : function(nodeInfo){
28274         if(typeof nodeInfo == "string"){
28275             return document.getElementById(nodeInfo);
28276         }else if(typeof nodeInfo == "number"){
28277             return this.nodes[nodeInfo];
28278         }
28279         return nodeInfo;
28280     },
28281
28282     /**
28283      * Gets a range template nodes.
28284      * @param {Number} startIndex
28285      * @param {Number} endIndex
28286      * @return {Array} An array of nodes
28287      */
28288     getNodes : function(start, end){
28289         var ns = this.nodes;
28290         start = start || 0;
28291         end = typeof end == "undefined" ? ns.length - 1 : end;
28292         var nodes = [];
28293         if(start <= end){
28294             for(var i = start; i <= end; i++){
28295                 nodes.push(ns[i]);
28296             }
28297         } else{
28298             for(var i = start; i >= end; i--){
28299                 nodes.push(ns[i]);
28300             }
28301         }
28302         return nodes;
28303     },
28304
28305     /**
28306      * Finds the index of the passed node
28307      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28308      * @return {Number} The index of the node or -1
28309      */
28310     indexOf : function(node){
28311         node = this.getNode(node);
28312         if(typeof node.nodeIndex == "number"){
28313             return node.nodeIndex;
28314         }
28315         var ns = this.nodes;
28316         for(var i = 0, len = ns.length; i < len; i++){
28317             if(ns[i] == node){
28318                 return i;
28319             }
28320         }
28321         return -1;
28322     }
28323 });
28324 /*
28325  * Based on:
28326  * Ext JS Library 1.1.1
28327  * Copyright(c) 2006-2007, Ext JS, LLC.
28328  *
28329  * Originally Released Under LGPL - original licence link has changed is not relivant.
28330  *
28331  * Fork - LGPL
28332  * <script type="text/javascript">
28333  */
28334
28335 /**
28336  * @class Roo.JsonView
28337  * @extends Roo.View
28338  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28339 <pre><code>
28340 var view = new Roo.JsonView({
28341     container: "my-element",
28342     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28343     multiSelect: true, 
28344     jsonRoot: "data" 
28345 });
28346
28347 // listen for node click?
28348 view.on("click", function(vw, index, node, e){
28349     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28350 });
28351
28352 // direct load of JSON data
28353 view.load("foobar.php");
28354
28355 // Example from my blog list
28356 var tpl = new Roo.Template(
28357     '&lt;div class="entry"&gt;' +
28358     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28359     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28360     "&lt;/div&gt;&lt;hr /&gt;"
28361 );
28362
28363 var moreView = new Roo.JsonView({
28364     container :  "entry-list", 
28365     template : tpl,
28366     jsonRoot: "posts"
28367 });
28368 moreView.on("beforerender", this.sortEntries, this);
28369 moreView.load({
28370     url: "/blog/get-posts.php",
28371     params: "allposts=true",
28372     text: "Loading Blog Entries..."
28373 });
28374 </code></pre>
28375
28376 * Note: old code is supported with arguments : (container, template, config)
28377
28378
28379  * @constructor
28380  * Create a new JsonView
28381  * 
28382  * @param {Object} config The config object
28383  * 
28384  */
28385 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28386     
28387     
28388     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28389
28390     var um = this.el.getUpdateManager();
28391     um.setRenderer(this);
28392     um.on("update", this.onLoad, this);
28393     um.on("failure", this.onLoadException, this);
28394
28395     /**
28396      * @event beforerender
28397      * Fires before rendering of the downloaded JSON data.
28398      * @param {Roo.JsonView} this
28399      * @param {Object} data The JSON data loaded
28400      */
28401     /**
28402      * @event load
28403      * Fires when data is loaded.
28404      * @param {Roo.JsonView} this
28405      * @param {Object} data The JSON data loaded
28406      * @param {Object} response The raw Connect response object
28407      */
28408     /**
28409      * @event loadexception
28410      * Fires when loading fails.
28411      * @param {Roo.JsonView} this
28412      * @param {Object} response The raw Connect response object
28413      */
28414     this.addEvents({
28415         'beforerender' : true,
28416         'load' : true,
28417         'loadexception' : true
28418     });
28419 };
28420 Roo.extend(Roo.JsonView, Roo.View, {
28421     /**
28422      * @type {String} The root property in the loaded JSON object that contains the data
28423      */
28424     jsonRoot : "",
28425
28426     /**
28427      * Refreshes the view.
28428      */
28429     refresh : function(){
28430         this.clearSelections();
28431         this.el.update("");
28432         var html = [];
28433         var o = this.jsonData;
28434         if(o && o.length > 0){
28435             for(var i = 0, len = o.length; i < len; i++){
28436                 var data = this.prepareData(o[i], i, o);
28437                 html[html.length] = this.tpl.apply(data);
28438             }
28439         }else{
28440             html.push(this.emptyText);
28441         }
28442         this.el.update(html.join(""));
28443         this.nodes = this.el.dom.childNodes;
28444         this.updateIndexes(0);
28445     },
28446
28447     /**
28448      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
28449      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
28450      <pre><code>
28451      view.load({
28452          url: "your-url.php",
28453          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28454          callback: yourFunction,
28455          scope: yourObject, //(optional scope)
28456          discardUrl: false,
28457          nocache: false,
28458          text: "Loading...",
28459          timeout: 30,
28460          scripts: false
28461      });
28462      </code></pre>
28463      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28464      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
28465      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
28466      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28467      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
28468      */
28469     load : function(){
28470         var um = this.el.getUpdateManager();
28471         um.update.apply(um, arguments);
28472     },
28473
28474     // note - render is a standard framework call...
28475     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28476     render : function(el, response){
28477         
28478         this.clearSelections();
28479         this.el.update("");
28480         var o;
28481         try{
28482             if (response != '') {
28483                 o = Roo.util.JSON.decode(response.responseText);
28484                 if(this.jsonRoot){
28485                     
28486                     o = o[this.jsonRoot];
28487                 }
28488             }
28489         } catch(e){
28490         }
28491         /**
28492          * The current JSON data or null
28493          */
28494         this.jsonData = o;
28495         this.beforeRender();
28496         this.refresh();
28497     },
28498
28499 /**
28500  * Get the number of records in the current JSON dataset
28501  * @return {Number}
28502  */
28503     getCount : function(){
28504         return this.jsonData ? this.jsonData.length : 0;
28505     },
28506
28507 /**
28508  * Returns the JSON object for the specified node(s)
28509  * @param {HTMLElement/Array} node The node or an array of nodes
28510  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28511  * you get the JSON object for the node
28512  */
28513     getNodeData : function(node){
28514         if(node instanceof Array){
28515             var data = [];
28516             for(var i = 0, len = node.length; i < len; i++){
28517                 data.push(this.getNodeData(node[i]));
28518             }
28519             return data;
28520         }
28521         return this.jsonData[this.indexOf(node)] || null;
28522     },
28523
28524     beforeRender : function(){
28525         this.snapshot = this.jsonData;
28526         if(this.sortInfo){
28527             this.sort.apply(this, this.sortInfo);
28528         }
28529         this.fireEvent("beforerender", this, this.jsonData);
28530     },
28531
28532     onLoad : function(el, o){
28533         this.fireEvent("load", this, this.jsonData, o);
28534     },
28535
28536     onLoadException : function(el, o){
28537         this.fireEvent("loadexception", this, o);
28538     },
28539
28540 /**
28541  * Filter the data by a specific property.
28542  * @param {String} property A property on your JSON objects
28543  * @param {String/RegExp} value Either string that the property values
28544  * should start with, or a RegExp to test against the property
28545  */
28546     filter : function(property, value){
28547         if(this.jsonData){
28548             var data = [];
28549             var ss = this.snapshot;
28550             if(typeof value == "string"){
28551                 var vlen = value.length;
28552                 if(vlen == 0){
28553                     this.clearFilter();
28554                     return;
28555                 }
28556                 value = value.toLowerCase();
28557                 for(var i = 0, len = ss.length; i < len; i++){
28558                     var o = ss[i];
28559                     if(o[property].substr(0, vlen).toLowerCase() == value){
28560                         data.push(o);
28561                     }
28562                 }
28563             } else if(value.exec){ // regex?
28564                 for(var i = 0, len = ss.length; i < len; i++){
28565                     var o = ss[i];
28566                     if(value.test(o[property])){
28567                         data.push(o);
28568                     }
28569                 }
28570             } else{
28571                 return;
28572             }
28573             this.jsonData = data;
28574             this.refresh();
28575         }
28576     },
28577
28578 /**
28579  * Filter by a function. The passed function will be called with each
28580  * object in the current dataset. If the function returns true the value is kept,
28581  * otherwise it is filtered.
28582  * @param {Function} fn
28583  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28584  */
28585     filterBy : function(fn, scope){
28586         if(this.jsonData){
28587             var data = [];
28588             var ss = this.snapshot;
28589             for(var i = 0, len = ss.length; i < len; i++){
28590                 var o = ss[i];
28591                 if(fn.call(scope || this, o)){
28592                     data.push(o);
28593                 }
28594             }
28595             this.jsonData = data;
28596             this.refresh();
28597         }
28598     },
28599
28600 /**
28601  * Clears the current filter.
28602  */
28603     clearFilter : function(){
28604         if(this.snapshot && this.jsonData != this.snapshot){
28605             this.jsonData = this.snapshot;
28606             this.refresh();
28607         }
28608     },
28609
28610
28611 /**
28612  * Sorts the data for this view and refreshes it.
28613  * @param {String} property A property on your JSON objects to sort on
28614  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28615  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28616  */
28617     sort : function(property, dir, sortType){
28618         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28619         if(this.jsonData){
28620             var p = property;
28621             var dsc = dir && dir.toLowerCase() == "desc";
28622             var f = function(o1, o2){
28623                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28624                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28625                 ;
28626                 if(v1 < v2){
28627                     return dsc ? +1 : -1;
28628                 } else if(v1 > v2){
28629                     return dsc ? -1 : +1;
28630                 } else{
28631                     return 0;
28632                 }
28633             };
28634             this.jsonData.sort(f);
28635             this.refresh();
28636             if(this.jsonData != this.snapshot){
28637                 this.snapshot.sort(f);
28638             }
28639         }
28640     }
28641 });/*
28642  * Based on:
28643  * Ext JS Library 1.1.1
28644  * Copyright(c) 2006-2007, Ext JS, LLC.
28645  *
28646  * Originally Released Under LGPL - original licence link has changed is not relivant.
28647  *
28648  * Fork - LGPL
28649  * <script type="text/javascript">
28650  */
28651  
28652
28653 /**
28654  * @class Roo.ColorPalette
28655  * @extends Roo.Component
28656  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28657  * Here's an example of typical usage:
28658  * <pre><code>
28659 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28660 cp.render('my-div');
28661
28662 cp.on('select', function(palette, selColor){
28663     // do something with selColor
28664 });
28665 </code></pre>
28666  * @constructor
28667  * Create a new ColorPalette
28668  * @param {Object} config The config object
28669  */
28670 Roo.ColorPalette = function(config){
28671     Roo.ColorPalette.superclass.constructor.call(this, config);
28672     this.addEvents({
28673         /**
28674              * @event select
28675              * Fires when a color is selected
28676              * @param {ColorPalette} this
28677              * @param {String} color The 6-digit color hex code (without the # symbol)
28678              */
28679         select: true
28680     });
28681
28682     if(this.handler){
28683         this.on("select", this.handler, this.scope, true);
28684     }
28685 };
28686 Roo.extend(Roo.ColorPalette, Roo.Component, {
28687     /**
28688      * @cfg {String} itemCls
28689      * The CSS class to apply to the containing element (defaults to "x-color-palette")
28690      */
28691     itemCls : "x-color-palette",
28692     /**
28693      * @cfg {String} value
28694      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
28695      * the hex codes are case-sensitive.
28696      */
28697     value : null,
28698     clickEvent:'click',
28699     // private
28700     ctype: "Roo.ColorPalette",
28701
28702     /**
28703      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
28704      */
28705     allowReselect : false,
28706
28707     /**
28708      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
28709      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
28710      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
28711      * of colors with the width setting until the box is symmetrical.</p>
28712      * <p>You can override individual colors if needed:</p>
28713      * <pre><code>
28714 var cp = new Roo.ColorPalette();
28715 cp.colors[0] = "FF0000";  // change the first box to red
28716 </code></pre>
28717
28718 Or you can provide a custom array of your own for complete control:
28719 <pre><code>
28720 var cp = new Roo.ColorPalette();
28721 cp.colors = ["000000", "993300", "333300"];
28722 </code></pre>
28723      * @type Array
28724      */
28725     colors : [
28726         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
28727         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
28728         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
28729         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
28730         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
28731     ],
28732
28733     // private
28734     onRender : function(container, position){
28735         var t = new Roo.MasterTemplate(
28736             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
28737         );
28738         var c = this.colors;
28739         for(var i = 0, len = c.length; i < len; i++){
28740             t.add([c[i]]);
28741         }
28742         var el = document.createElement("div");
28743         el.className = this.itemCls;
28744         t.overwrite(el);
28745         container.dom.insertBefore(el, position);
28746         this.el = Roo.get(el);
28747         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
28748         if(this.clickEvent != 'click'){
28749             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
28750         }
28751     },
28752
28753     // private
28754     afterRender : function(){
28755         Roo.ColorPalette.superclass.afterRender.call(this);
28756         if(this.value){
28757             var s = this.value;
28758             this.value = null;
28759             this.select(s);
28760         }
28761     },
28762
28763     // private
28764     handleClick : function(e, t){
28765         e.preventDefault();
28766         if(!this.disabled){
28767             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
28768             this.select(c.toUpperCase());
28769         }
28770     },
28771
28772     /**
28773      * Selects the specified color in the palette (fires the select event)
28774      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
28775      */
28776     select : function(color){
28777         color = color.replace("#", "");
28778         if(color != this.value || this.allowReselect){
28779             var el = this.el;
28780             if(this.value){
28781                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
28782             }
28783             el.child("a.color-"+color).addClass("x-color-palette-sel");
28784             this.value = color;
28785             this.fireEvent("select", this, color);
28786         }
28787     }
28788 });/*
28789  * Based on:
28790  * Ext JS Library 1.1.1
28791  * Copyright(c) 2006-2007, Ext JS, LLC.
28792  *
28793  * Originally Released Under LGPL - original licence link has changed is not relivant.
28794  *
28795  * Fork - LGPL
28796  * <script type="text/javascript">
28797  */
28798  
28799 /**
28800  * @class Roo.DatePicker
28801  * @extends Roo.Component
28802  * Simple date picker class.
28803  * @constructor
28804  * Create a new DatePicker
28805  * @param {Object} config The config object
28806  */
28807 Roo.DatePicker = function(config){
28808     Roo.DatePicker.superclass.constructor.call(this, config);
28809
28810     this.value = config && config.value ?
28811                  config.value.clearTime() : new Date().clearTime();
28812
28813     this.addEvents({
28814         /**
28815              * @event select
28816              * Fires when a date is selected
28817              * @param {DatePicker} this
28818              * @param {Date} date The selected date
28819              */
28820         'select': true,
28821         /**
28822              * @event monthchange
28823              * Fires when the displayed month changes 
28824              * @param {DatePicker} this
28825              * @param {Date} date The selected month
28826              */
28827         'monthchange': true
28828     });
28829
28830     if(this.handler){
28831         this.on("select", this.handler,  this.scope || this);
28832     }
28833     // build the disabledDatesRE
28834     if(!this.disabledDatesRE && this.disabledDates){
28835         var dd = this.disabledDates;
28836         var re = "(?:";
28837         for(var i = 0; i < dd.length; i++){
28838             re += dd[i];
28839             if(i != dd.length-1) {
28840                 re += "|";
28841             }
28842         }
28843         this.disabledDatesRE = new RegExp(re + ")");
28844     }
28845 };
28846
28847 Roo.extend(Roo.DatePicker, Roo.Component, {
28848     /**
28849      * @cfg {String} todayText
28850      * The text to display on the button that selects the current date (defaults to "Today")
28851      */
28852     todayText : "Today",
28853     /**
28854      * @cfg {String} okText
28855      * The text to display on the ok button
28856      */
28857     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
28858     /**
28859      * @cfg {String} cancelText
28860      * The text to display on the cancel button
28861      */
28862     cancelText : "Cancel",
28863     /**
28864      * @cfg {String} todayTip
28865      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
28866      */
28867     todayTip : "{0} (Spacebar)",
28868     /**
28869      * @cfg {Date} minDate
28870      * Minimum allowable date (JavaScript date object, defaults to null)
28871      */
28872     minDate : null,
28873     /**
28874      * @cfg {Date} maxDate
28875      * Maximum allowable date (JavaScript date object, defaults to null)
28876      */
28877     maxDate : null,
28878     /**
28879      * @cfg {String} minText
28880      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28881      */
28882     minText : "This date is before the minimum date",
28883     /**
28884      * @cfg {String} maxText
28885      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28886      */
28887     maxText : "This date is after the maximum date",
28888     /**
28889      * @cfg {String} format
28890      * The default date format string which can be overriden for localization support.  The format must be
28891      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28892      */
28893     format : "m/d/y",
28894     /**
28895      * @cfg {Array} disabledDays
28896      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28897      */
28898     disabledDays : null,
28899     /**
28900      * @cfg {String} disabledDaysText
28901      * The tooltip to display when the date falls on a disabled day (defaults to "")
28902      */
28903     disabledDaysText : "",
28904     /**
28905      * @cfg {RegExp} disabledDatesRE
28906      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28907      */
28908     disabledDatesRE : null,
28909     /**
28910      * @cfg {String} disabledDatesText
28911      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28912      */
28913     disabledDatesText : "",
28914     /**
28915      * @cfg {Boolean} constrainToViewport
28916      * True to constrain the date picker to the viewport (defaults to true)
28917      */
28918     constrainToViewport : true,
28919     /**
28920      * @cfg {Array} monthNames
28921      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28922      */
28923     monthNames : Date.monthNames,
28924     /**
28925      * @cfg {Array} dayNames
28926      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28927      */
28928     dayNames : Date.dayNames,
28929     /**
28930      * @cfg {String} nextText
28931      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28932      */
28933     nextText: 'Next Month (Control+Right)',
28934     /**
28935      * @cfg {String} prevText
28936      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28937      */
28938     prevText: 'Previous Month (Control+Left)',
28939     /**
28940      * @cfg {String} monthYearText
28941      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28942      */
28943     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28944     /**
28945      * @cfg {Number} startDay
28946      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28947      */
28948     startDay : 0,
28949     /**
28950      * @cfg {Bool} showClear
28951      * Show a clear button (usefull for date form elements that can be blank.)
28952      */
28953     
28954     showClear: false,
28955     
28956     /**
28957      * Sets the value of the date field
28958      * @param {Date} value The date to set
28959      */
28960     setValue : function(value){
28961         var old = this.value;
28962         
28963         if (typeof(value) == 'string') {
28964          
28965             value = Date.parseDate(value, this.format);
28966         }
28967         if (!value) {
28968             value = new Date();
28969         }
28970         
28971         this.value = value.clearTime(true);
28972         if(this.el){
28973             this.update(this.value);
28974         }
28975     },
28976
28977     /**
28978      * Gets the current selected value of the date field
28979      * @return {Date} The selected date
28980      */
28981     getValue : function(){
28982         return this.value;
28983     },
28984
28985     // private
28986     focus : function(){
28987         if(this.el){
28988             this.update(this.activeDate);
28989         }
28990     },
28991
28992     // privateval
28993     onRender : function(container, position){
28994         
28995         var m = [
28996              '<table cellspacing="0">',
28997                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
28998                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28999         var dn = this.dayNames;
29000         for(var i = 0; i < 7; i++){
29001             var d = this.startDay+i;
29002             if(d > 6){
29003                 d = d-7;
29004             }
29005             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29006         }
29007         m[m.length] = "</tr></thead><tbody><tr>";
29008         for(var i = 0; i < 42; i++) {
29009             if(i % 7 == 0 && i != 0){
29010                 m[m.length] = "</tr><tr>";
29011             }
29012             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29013         }
29014         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29015             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29016
29017         var el = document.createElement("div");
29018         el.className = "x-date-picker";
29019         el.innerHTML = m.join("");
29020
29021         container.dom.insertBefore(el, position);
29022
29023         this.el = Roo.get(el);
29024         this.eventEl = Roo.get(el.firstChild);
29025
29026         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29027             handler: this.showPrevMonth,
29028             scope: this,
29029             preventDefault:true,
29030             stopDefault:true
29031         });
29032
29033         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29034             handler: this.showNextMonth,
29035             scope: this,
29036             preventDefault:true,
29037             stopDefault:true
29038         });
29039
29040         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29041
29042         this.monthPicker = this.el.down('div.x-date-mp');
29043         this.monthPicker.enableDisplayMode('block');
29044         
29045         var kn = new Roo.KeyNav(this.eventEl, {
29046             "left" : function(e){
29047                 e.ctrlKey ?
29048                     this.showPrevMonth() :
29049                     this.update(this.activeDate.add("d", -1));
29050             },
29051
29052             "right" : function(e){
29053                 e.ctrlKey ?
29054                     this.showNextMonth() :
29055                     this.update(this.activeDate.add("d", 1));
29056             },
29057
29058             "up" : function(e){
29059                 e.ctrlKey ?
29060                     this.showNextYear() :
29061                     this.update(this.activeDate.add("d", -7));
29062             },
29063
29064             "down" : function(e){
29065                 e.ctrlKey ?
29066                     this.showPrevYear() :
29067                     this.update(this.activeDate.add("d", 7));
29068             },
29069
29070             "pageUp" : function(e){
29071                 this.showNextMonth();
29072             },
29073
29074             "pageDown" : function(e){
29075                 this.showPrevMonth();
29076             },
29077
29078             "enter" : function(e){
29079                 e.stopPropagation();
29080                 return true;
29081             },
29082
29083             scope : this
29084         });
29085
29086         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29087
29088         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29089
29090         this.el.unselectable();
29091         
29092         this.cells = this.el.select("table.x-date-inner tbody td");
29093         this.textNodes = this.el.query("table.x-date-inner tbody span");
29094
29095         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29096             text: "&#160;",
29097             tooltip: this.monthYearText
29098         });
29099
29100         this.mbtn.on('click', this.showMonthPicker, this);
29101         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29102
29103
29104         var today = (new Date()).dateFormat(this.format);
29105         
29106         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29107         if (this.showClear) {
29108             baseTb.add( new Roo.Toolbar.Fill());
29109         }
29110         baseTb.add({
29111             text: String.format(this.todayText, today),
29112             tooltip: String.format(this.todayTip, today),
29113             handler: this.selectToday,
29114             scope: this
29115         });
29116         
29117         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29118             
29119         //});
29120         if (this.showClear) {
29121             
29122             baseTb.add( new Roo.Toolbar.Fill());
29123             baseTb.add({
29124                 text: '&#160;',
29125                 cls: 'x-btn-icon x-btn-clear',
29126                 handler: function() {
29127                     //this.value = '';
29128                     this.fireEvent("select", this, '');
29129                 },
29130                 scope: this
29131             });
29132         }
29133         
29134         
29135         if(Roo.isIE){
29136             this.el.repaint();
29137         }
29138         this.update(this.value);
29139     },
29140
29141     createMonthPicker : function(){
29142         if(!this.monthPicker.dom.firstChild){
29143             var buf = ['<table border="0" cellspacing="0">'];
29144             for(var i = 0; i < 6; i++){
29145                 buf.push(
29146                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29147                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29148                     i == 0 ?
29149                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
29150                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29151                 );
29152             }
29153             buf.push(
29154                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29155                     this.okText,
29156                     '</button><button type="button" class="x-date-mp-cancel">',
29157                     this.cancelText,
29158                     '</button></td></tr>',
29159                 '</table>'
29160             );
29161             this.monthPicker.update(buf.join(''));
29162             this.monthPicker.on('click', this.onMonthClick, this);
29163             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29164
29165             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29166             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29167
29168             this.mpMonths.each(function(m, a, i){
29169                 i += 1;
29170                 if((i%2) == 0){
29171                     m.dom.xmonth = 5 + Math.round(i * .5);
29172                 }else{
29173                     m.dom.xmonth = Math.round((i-1) * .5);
29174                 }
29175             });
29176         }
29177     },
29178
29179     showMonthPicker : function(){
29180         this.createMonthPicker();
29181         var size = this.el.getSize();
29182         this.monthPicker.setSize(size);
29183         this.monthPicker.child('table').setSize(size);
29184
29185         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29186         this.updateMPMonth(this.mpSelMonth);
29187         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29188         this.updateMPYear(this.mpSelYear);
29189
29190         this.monthPicker.slideIn('t', {duration:.2});
29191     },
29192
29193     updateMPYear : function(y){
29194         this.mpyear = y;
29195         var ys = this.mpYears.elements;
29196         for(var i = 1; i <= 10; i++){
29197             var td = ys[i-1], y2;
29198             if((i%2) == 0){
29199                 y2 = y + Math.round(i * .5);
29200                 td.firstChild.innerHTML = y2;
29201                 td.xyear = y2;
29202             }else{
29203                 y2 = y - (5-Math.round(i * .5));
29204                 td.firstChild.innerHTML = y2;
29205                 td.xyear = y2;
29206             }
29207             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29208         }
29209     },
29210
29211     updateMPMonth : function(sm){
29212         this.mpMonths.each(function(m, a, i){
29213             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29214         });
29215     },
29216
29217     selectMPMonth: function(m){
29218         
29219     },
29220
29221     onMonthClick : function(e, t){
29222         e.stopEvent();
29223         var el = new Roo.Element(t), pn;
29224         if(el.is('button.x-date-mp-cancel')){
29225             this.hideMonthPicker();
29226         }
29227         else if(el.is('button.x-date-mp-ok')){
29228             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29229             this.hideMonthPicker();
29230         }
29231         else if(pn = el.up('td.x-date-mp-month', 2)){
29232             this.mpMonths.removeClass('x-date-mp-sel');
29233             pn.addClass('x-date-mp-sel');
29234             this.mpSelMonth = pn.dom.xmonth;
29235         }
29236         else if(pn = el.up('td.x-date-mp-year', 2)){
29237             this.mpYears.removeClass('x-date-mp-sel');
29238             pn.addClass('x-date-mp-sel');
29239             this.mpSelYear = pn.dom.xyear;
29240         }
29241         else if(el.is('a.x-date-mp-prev')){
29242             this.updateMPYear(this.mpyear-10);
29243         }
29244         else if(el.is('a.x-date-mp-next')){
29245             this.updateMPYear(this.mpyear+10);
29246         }
29247     },
29248
29249     onMonthDblClick : function(e, t){
29250         e.stopEvent();
29251         var el = new Roo.Element(t), pn;
29252         if(pn = el.up('td.x-date-mp-month', 2)){
29253             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29254             this.hideMonthPicker();
29255         }
29256         else if(pn = el.up('td.x-date-mp-year', 2)){
29257             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29258             this.hideMonthPicker();
29259         }
29260     },
29261
29262     hideMonthPicker : function(disableAnim){
29263         if(this.monthPicker){
29264             if(disableAnim === true){
29265                 this.monthPicker.hide();
29266             }else{
29267                 this.monthPicker.slideOut('t', {duration:.2});
29268             }
29269         }
29270     },
29271
29272     // private
29273     showPrevMonth : function(e){
29274         this.update(this.activeDate.add("mo", -1));
29275     },
29276
29277     // private
29278     showNextMonth : function(e){
29279         this.update(this.activeDate.add("mo", 1));
29280     },
29281
29282     // private
29283     showPrevYear : function(){
29284         this.update(this.activeDate.add("y", -1));
29285     },
29286
29287     // private
29288     showNextYear : function(){
29289         this.update(this.activeDate.add("y", 1));
29290     },
29291
29292     // private
29293     handleMouseWheel : function(e){
29294         var delta = e.getWheelDelta();
29295         if(delta > 0){
29296             this.showPrevMonth();
29297             e.stopEvent();
29298         } else if(delta < 0){
29299             this.showNextMonth();
29300             e.stopEvent();
29301         }
29302     },
29303
29304     // private
29305     handleDateClick : function(e, t){
29306         e.stopEvent();
29307         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29308             this.setValue(new Date(t.dateValue));
29309             this.fireEvent("select", this, this.value);
29310         }
29311     },
29312
29313     // private
29314     selectToday : function(){
29315         this.setValue(new Date().clearTime());
29316         this.fireEvent("select", this, this.value);
29317     },
29318
29319     // private
29320     update : function(date)
29321     {
29322         var vd = this.activeDate;
29323         this.activeDate = date;
29324         if(vd && this.el){
29325             var t = date.getTime();
29326             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29327                 this.cells.removeClass("x-date-selected");
29328                 this.cells.each(function(c){
29329                    if(c.dom.firstChild.dateValue == t){
29330                        c.addClass("x-date-selected");
29331                        setTimeout(function(){
29332                             try{c.dom.firstChild.focus();}catch(e){}
29333                        }, 50);
29334                        return false;
29335                    }
29336                 });
29337                 return;
29338             }
29339         }
29340         
29341         var days = date.getDaysInMonth();
29342         var firstOfMonth = date.getFirstDateOfMonth();
29343         var startingPos = firstOfMonth.getDay()-this.startDay;
29344
29345         if(startingPos <= this.startDay){
29346             startingPos += 7;
29347         }
29348
29349         var pm = date.add("mo", -1);
29350         var prevStart = pm.getDaysInMonth()-startingPos;
29351
29352         var cells = this.cells.elements;
29353         var textEls = this.textNodes;
29354         days += startingPos;
29355
29356         // convert everything to numbers so it's fast
29357         var day = 86400000;
29358         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29359         var today = new Date().clearTime().getTime();
29360         var sel = date.clearTime().getTime();
29361         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29362         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29363         var ddMatch = this.disabledDatesRE;
29364         var ddText = this.disabledDatesText;
29365         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29366         var ddaysText = this.disabledDaysText;
29367         var format = this.format;
29368
29369         var setCellClass = function(cal, cell){
29370             cell.title = "";
29371             var t = d.getTime();
29372             cell.firstChild.dateValue = t;
29373             if(t == today){
29374                 cell.className += " x-date-today";
29375                 cell.title = cal.todayText;
29376             }
29377             if(t == sel){
29378                 cell.className += " x-date-selected";
29379                 setTimeout(function(){
29380                     try{cell.firstChild.focus();}catch(e){}
29381                 }, 50);
29382             }
29383             // disabling
29384             if(t < min) {
29385                 cell.className = " x-date-disabled";
29386                 cell.title = cal.minText;
29387                 return;
29388             }
29389             if(t > max) {
29390                 cell.className = " x-date-disabled";
29391                 cell.title = cal.maxText;
29392                 return;
29393             }
29394             if(ddays){
29395                 if(ddays.indexOf(d.getDay()) != -1){
29396                     cell.title = ddaysText;
29397                     cell.className = " x-date-disabled";
29398                 }
29399             }
29400             if(ddMatch && format){
29401                 var fvalue = d.dateFormat(format);
29402                 if(ddMatch.test(fvalue)){
29403                     cell.title = ddText.replace("%0", fvalue);
29404                     cell.className = " x-date-disabled";
29405                 }
29406             }
29407         };
29408
29409         var i = 0;
29410         for(; i < startingPos; i++) {
29411             textEls[i].innerHTML = (++prevStart);
29412             d.setDate(d.getDate()+1);
29413             cells[i].className = "x-date-prevday";
29414             setCellClass(this, cells[i]);
29415         }
29416         for(; i < days; i++){
29417             intDay = i - startingPos + 1;
29418             textEls[i].innerHTML = (intDay);
29419             d.setDate(d.getDate()+1);
29420             cells[i].className = "x-date-active";
29421             setCellClass(this, cells[i]);
29422         }
29423         var extraDays = 0;
29424         for(; i < 42; i++) {
29425              textEls[i].innerHTML = (++extraDays);
29426              d.setDate(d.getDate()+1);
29427              cells[i].className = "x-date-nextday";
29428              setCellClass(this, cells[i]);
29429         }
29430
29431         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29432         this.fireEvent('monthchange', this, date);
29433         
29434         if(!this.internalRender){
29435             var main = this.el.dom.firstChild;
29436             var w = main.offsetWidth;
29437             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29438             Roo.fly(main).setWidth(w);
29439             this.internalRender = true;
29440             // opera does not respect the auto grow header center column
29441             // then, after it gets a width opera refuses to recalculate
29442             // without a second pass
29443             if(Roo.isOpera && !this.secondPass){
29444                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29445                 this.secondPass = true;
29446                 this.update.defer(10, this, [date]);
29447             }
29448         }
29449         
29450         
29451     }
29452 });        /*
29453  * Based on:
29454  * Ext JS Library 1.1.1
29455  * Copyright(c) 2006-2007, Ext JS, LLC.
29456  *
29457  * Originally Released Under LGPL - original licence link has changed is not relivant.
29458  *
29459  * Fork - LGPL
29460  * <script type="text/javascript">
29461  */
29462 /**
29463  * @class Roo.TabPanel
29464  * @extends Roo.util.Observable
29465  * A lightweight tab container.
29466  * <br><br>
29467  * Usage:
29468  * <pre><code>
29469 // basic tabs 1, built from existing content
29470 var tabs = new Roo.TabPanel("tabs1");
29471 tabs.addTab("script", "View Script");
29472 tabs.addTab("markup", "View Markup");
29473 tabs.activate("script");
29474
29475 // more advanced tabs, built from javascript
29476 var jtabs = new Roo.TabPanel("jtabs");
29477 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29478
29479 // set up the UpdateManager
29480 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29481 var updater = tab2.getUpdateManager();
29482 updater.setDefaultUrl("ajax1.htm");
29483 tab2.on('activate', updater.refresh, updater, true);
29484
29485 // Use setUrl for Ajax loading
29486 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29487 tab3.setUrl("ajax2.htm", null, true);
29488
29489 // Disabled tab
29490 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29491 tab4.disable();
29492
29493 jtabs.activate("jtabs-1");
29494  * </code></pre>
29495  * @constructor
29496  * Create a new TabPanel.
29497  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29498  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29499  */
29500 Roo.TabPanel = function(container, config){
29501     /**
29502     * The container element for this TabPanel.
29503     * @type Roo.Element
29504     */
29505     this.el = Roo.get(container, true);
29506     if(config){
29507         if(typeof config == "boolean"){
29508             this.tabPosition = config ? "bottom" : "top";
29509         }else{
29510             Roo.apply(this, config);
29511         }
29512     }
29513     if(this.tabPosition == "bottom"){
29514         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29515         this.el.addClass("x-tabs-bottom");
29516     }
29517     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29518     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29519     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29520     if(Roo.isIE){
29521         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29522     }
29523     if(this.tabPosition != "bottom"){
29524         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29525          * @type Roo.Element
29526          */
29527         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29528         this.el.addClass("x-tabs-top");
29529     }
29530     this.items = [];
29531
29532     this.bodyEl.setStyle("position", "relative");
29533
29534     this.active = null;
29535     this.activateDelegate = this.activate.createDelegate(this);
29536
29537     this.addEvents({
29538         /**
29539          * @event tabchange
29540          * Fires when the active tab changes
29541          * @param {Roo.TabPanel} this
29542          * @param {Roo.TabPanelItem} activePanel The new active tab
29543          */
29544         "tabchange": true,
29545         /**
29546          * @event beforetabchange
29547          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29548          * @param {Roo.TabPanel} this
29549          * @param {Object} e Set cancel to true on this object to cancel the tab change
29550          * @param {Roo.TabPanelItem} tab The tab being changed to
29551          */
29552         "beforetabchange" : true
29553     });
29554
29555     Roo.EventManager.onWindowResize(this.onResize, this);
29556     this.cpad = this.el.getPadding("lr");
29557     this.hiddenCount = 0;
29558
29559
29560     // toolbar on the tabbar support...
29561     if (this.toolbar) {
29562         var tcfg = this.toolbar;
29563         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29564         this.toolbar = new Roo.Toolbar(tcfg);
29565         if (Roo.isSafari) {
29566             var tbl = tcfg.container.child('table', true);
29567             tbl.setAttribute('width', '100%');
29568         }
29569         
29570     }
29571    
29572
29573
29574     Roo.TabPanel.superclass.constructor.call(this);
29575 };
29576
29577 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29578     /*
29579      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29580      */
29581     tabPosition : "top",
29582     /*
29583      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29584      */
29585     currentTabWidth : 0,
29586     /*
29587      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29588      */
29589     minTabWidth : 40,
29590     /*
29591      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29592      */
29593     maxTabWidth : 250,
29594     /*
29595      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29596      */
29597     preferredTabWidth : 175,
29598     /*
29599      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29600      */
29601     resizeTabs : false,
29602     /*
29603      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29604      */
29605     monitorResize : true,
29606     /*
29607      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29608      */
29609     toolbar : false,
29610
29611     /**
29612      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29613      * @param {String} id The id of the div to use <b>or create</b>
29614      * @param {String} text The text for the tab
29615      * @param {String} content (optional) Content to put in the TabPanelItem body
29616      * @param {Boolean} closable (optional) True to create a close icon on the tab
29617      * @return {Roo.TabPanelItem} The created TabPanelItem
29618      */
29619     addTab : function(id, text, content, closable){
29620         var item = new Roo.TabPanelItem(this, id, text, closable);
29621         this.addTabItem(item);
29622         if(content){
29623             item.setContent(content);
29624         }
29625         return item;
29626     },
29627
29628     /**
29629      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29630      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29631      * @return {Roo.TabPanelItem}
29632      */
29633     getTab : function(id){
29634         return this.items[id];
29635     },
29636
29637     /**
29638      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29639      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29640      */
29641     hideTab : function(id){
29642         var t = this.items[id];
29643         if(!t.isHidden()){
29644            t.setHidden(true);
29645            this.hiddenCount++;
29646            this.autoSizeTabs();
29647         }
29648     },
29649
29650     /**
29651      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29652      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29653      */
29654     unhideTab : function(id){
29655         var t = this.items[id];
29656         if(t.isHidden()){
29657            t.setHidden(false);
29658            this.hiddenCount--;
29659            this.autoSizeTabs();
29660         }
29661     },
29662
29663     /**
29664      * Adds an existing {@link Roo.TabPanelItem}.
29665      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29666      */
29667     addTabItem : function(item){
29668         this.items[item.id] = item;
29669         this.items.push(item);
29670         if(this.resizeTabs){
29671            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29672            this.autoSizeTabs();
29673         }else{
29674             item.autoSize();
29675         }
29676     },
29677
29678     /**
29679      * Removes a {@link Roo.TabPanelItem}.
29680      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29681      */
29682     removeTab : function(id){
29683         var items = this.items;
29684         var tab = items[id];
29685         if(!tab) { return; }
29686         var index = items.indexOf(tab);
29687         if(this.active == tab && items.length > 1){
29688             var newTab = this.getNextAvailable(index);
29689             if(newTab) {
29690                 newTab.activate();
29691             }
29692         }
29693         this.stripEl.dom.removeChild(tab.pnode.dom);
29694         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
29695             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
29696         }
29697         items.splice(index, 1);
29698         delete this.items[tab.id];
29699         tab.fireEvent("close", tab);
29700         tab.purgeListeners();
29701         this.autoSizeTabs();
29702     },
29703
29704     getNextAvailable : function(start){
29705         var items = this.items;
29706         var index = start;
29707         // look for a next tab that will slide over to
29708         // replace the one being removed
29709         while(index < items.length){
29710             var item = items[++index];
29711             if(item && !item.isHidden()){
29712                 return item;
29713             }
29714         }
29715         // if one isn't found select the previous tab (on the left)
29716         index = start;
29717         while(index >= 0){
29718             var item = items[--index];
29719             if(item && !item.isHidden()){
29720                 return item;
29721             }
29722         }
29723         return null;
29724     },
29725
29726     /**
29727      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
29728      * @param {String/Number} id The id or index of the TabPanelItem to disable.
29729      */
29730     disableTab : function(id){
29731         var tab = this.items[id];
29732         if(tab && this.active != tab){
29733             tab.disable();
29734         }
29735     },
29736
29737     /**
29738      * Enables a {@link Roo.TabPanelItem} that is disabled.
29739      * @param {String/Number} id The id or index of the TabPanelItem to enable.
29740      */
29741     enableTab : function(id){
29742         var tab = this.items[id];
29743         tab.enable();
29744     },
29745
29746     /**
29747      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
29748      * @param {String/Number} id The id or index of the TabPanelItem to activate.
29749      * @return {Roo.TabPanelItem} The TabPanelItem.
29750      */
29751     activate : function(id){
29752         var tab = this.items[id];
29753         if(!tab){
29754             return null;
29755         }
29756         if(tab == this.active || tab.disabled){
29757             return tab;
29758         }
29759         var e = {};
29760         this.fireEvent("beforetabchange", this, e, tab);
29761         if(e.cancel !== true && !tab.disabled){
29762             if(this.active){
29763                 this.active.hide();
29764             }
29765             this.active = this.items[id];
29766             this.active.show();
29767             this.fireEvent("tabchange", this, this.active);
29768         }
29769         return tab;
29770     },
29771
29772     /**
29773      * Gets the active {@link Roo.TabPanelItem}.
29774      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
29775      */
29776     getActiveTab : function(){
29777         return this.active;
29778     },
29779
29780     /**
29781      * Updates the tab body element to fit the height of the container element
29782      * for overflow scrolling
29783      * @param {Number} targetHeight (optional) Override the starting height from the elements height
29784      */
29785     syncHeight : function(targetHeight){
29786         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29787         var bm = this.bodyEl.getMargins();
29788         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
29789         this.bodyEl.setHeight(newHeight);
29790         return newHeight;
29791     },
29792
29793     onResize : function(){
29794         if(this.monitorResize){
29795             this.autoSizeTabs();
29796         }
29797     },
29798
29799     /**
29800      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
29801      */
29802     beginUpdate : function(){
29803         this.updating = true;
29804     },
29805
29806     /**
29807      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
29808      */
29809     endUpdate : function(){
29810         this.updating = false;
29811         this.autoSizeTabs();
29812     },
29813
29814     /**
29815      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
29816      */
29817     autoSizeTabs : function(){
29818         var count = this.items.length;
29819         var vcount = count - this.hiddenCount;
29820         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
29821             return;
29822         }
29823         var w = Math.max(this.el.getWidth() - this.cpad, 10);
29824         var availWidth = Math.floor(w / vcount);
29825         var b = this.stripBody;
29826         if(b.getWidth() > w){
29827             var tabs = this.items;
29828             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
29829             if(availWidth < this.minTabWidth){
29830                 /*if(!this.sleft){    // incomplete scrolling code
29831                     this.createScrollButtons();
29832                 }
29833                 this.showScroll();
29834                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
29835             }
29836         }else{
29837             if(this.currentTabWidth < this.preferredTabWidth){
29838                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
29839             }
29840         }
29841     },
29842
29843     /**
29844      * Returns the number of tabs in this TabPanel.
29845      * @return {Number}
29846      */
29847      getCount : function(){
29848          return this.items.length;
29849      },
29850
29851     /**
29852      * Resizes all the tabs to the passed width
29853      * @param {Number} The new width
29854      */
29855     setTabWidth : function(width){
29856         this.currentTabWidth = width;
29857         for(var i = 0, len = this.items.length; i < len; i++) {
29858                 if(!this.items[i].isHidden()) {
29859                 this.items[i].setWidth(width);
29860             }
29861         }
29862     },
29863
29864     /**
29865      * Destroys this TabPanel
29866      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
29867      */
29868     destroy : function(removeEl){
29869         Roo.EventManager.removeResizeListener(this.onResize, this);
29870         for(var i = 0, len = this.items.length; i < len; i++){
29871             this.items[i].purgeListeners();
29872         }
29873         if(removeEl === true){
29874             this.el.update("");
29875             this.el.remove();
29876         }
29877     }
29878 });
29879
29880 /**
29881  * @class Roo.TabPanelItem
29882  * @extends Roo.util.Observable
29883  * Represents an individual item (tab plus body) in a TabPanel.
29884  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29885  * @param {String} id The id of this TabPanelItem
29886  * @param {String} text The text for the tab of this TabPanelItem
29887  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29888  */
29889 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29890     /**
29891      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29892      * @type Roo.TabPanel
29893      */
29894     this.tabPanel = tabPanel;
29895     /**
29896      * The id for this TabPanelItem
29897      * @type String
29898      */
29899     this.id = id;
29900     /** @private */
29901     this.disabled = false;
29902     /** @private */
29903     this.text = text;
29904     /** @private */
29905     this.loaded = false;
29906     this.closable = closable;
29907
29908     /**
29909      * The body element for this TabPanelItem.
29910      * @type Roo.Element
29911      */
29912     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29913     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29914     this.bodyEl.setStyle("display", "block");
29915     this.bodyEl.setStyle("zoom", "1");
29916     this.hideAction();
29917
29918     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29919     /** @private */
29920     this.el = Roo.get(els.el, true);
29921     this.inner = Roo.get(els.inner, true);
29922     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29923     this.pnode = Roo.get(els.el.parentNode, true);
29924     this.el.on("mousedown", this.onTabMouseDown, this);
29925     this.el.on("click", this.onTabClick, this);
29926     /** @private */
29927     if(closable){
29928         var c = Roo.get(els.close, true);
29929         c.dom.title = this.closeText;
29930         c.addClassOnOver("close-over");
29931         c.on("click", this.closeClick, this);
29932      }
29933
29934     this.addEvents({
29935          /**
29936          * @event activate
29937          * Fires when this tab becomes the active tab.
29938          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29939          * @param {Roo.TabPanelItem} this
29940          */
29941         "activate": true,
29942         /**
29943          * @event beforeclose
29944          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29945          * @param {Roo.TabPanelItem} this
29946          * @param {Object} e Set cancel to true on this object to cancel the close.
29947          */
29948         "beforeclose": true,
29949         /**
29950          * @event close
29951          * Fires when this tab is closed.
29952          * @param {Roo.TabPanelItem} this
29953          */
29954          "close": true,
29955         /**
29956          * @event deactivate
29957          * Fires when this tab is no longer the active tab.
29958          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29959          * @param {Roo.TabPanelItem} this
29960          */
29961          "deactivate" : true
29962     });
29963     this.hidden = false;
29964
29965     Roo.TabPanelItem.superclass.constructor.call(this);
29966 };
29967
29968 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29969     purgeListeners : function(){
29970        Roo.util.Observable.prototype.purgeListeners.call(this);
29971        this.el.removeAllListeners();
29972     },
29973     /**
29974      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29975      */
29976     show : function(){
29977         this.pnode.addClass("on");
29978         this.showAction();
29979         if(Roo.isOpera){
29980             this.tabPanel.stripWrap.repaint();
29981         }
29982         this.fireEvent("activate", this.tabPanel, this);
29983     },
29984
29985     /**
29986      * Returns true if this tab is the active tab.
29987      * @return {Boolean}
29988      */
29989     isActive : function(){
29990         return this.tabPanel.getActiveTab() == this;
29991     },
29992
29993     /**
29994      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29995      */
29996     hide : function(){
29997         this.pnode.removeClass("on");
29998         this.hideAction();
29999         this.fireEvent("deactivate", this.tabPanel, this);
30000     },
30001
30002     hideAction : function(){
30003         this.bodyEl.hide();
30004         this.bodyEl.setStyle("position", "absolute");
30005         this.bodyEl.setLeft("-20000px");
30006         this.bodyEl.setTop("-20000px");
30007     },
30008
30009     showAction : function(){
30010         this.bodyEl.setStyle("position", "relative");
30011         this.bodyEl.setTop("");
30012         this.bodyEl.setLeft("");
30013         this.bodyEl.show();
30014     },
30015
30016     /**
30017      * Set the tooltip for the tab.
30018      * @param {String} tooltip The tab's tooltip
30019      */
30020     setTooltip : function(text){
30021         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30022             this.textEl.dom.qtip = text;
30023             this.textEl.dom.removeAttribute('title');
30024         }else{
30025             this.textEl.dom.title = text;
30026         }
30027     },
30028
30029     onTabClick : function(e){
30030         e.preventDefault();
30031         this.tabPanel.activate(this.id);
30032     },
30033
30034     onTabMouseDown : function(e){
30035         e.preventDefault();
30036         this.tabPanel.activate(this.id);
30037     },
30038
30039     getWidth : function(){
30040         return this.inner.getWidth();
30041     },
30042
30043     setWidth : function(width){
30044         var iwidth = width - this.pnode.getPadding("lr");
30045         this.inner.setWidth(iwidth);
30046         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30047         this.pnode.setWidth(width);
30048     },
30049
30050     /**
30051      * Show or hide the tab
30052      * @param {Boolean} hidden True to hide or false to show.
30053      */
30054     setHidden : function(hidden){
30055         this.hidden = hidden;
30056         this.pnode.setStyle("display", hidden ? "none" : "");
30057     },
30058
30059     /**
30060      * Returns true if this tab is "hidden"
30061      * @return {Boolean}
30062      */
30063     isHidden : function(){
30064         return this.hidden;
30065     },
30066
30067     /**
30068      * Returns the text for this tab
30069      * @return {String}
30070      */
30071     getText : function(){
30072         return this.text;
30073     },
30074
30075     autoSize : function(){
30076         //this.el.beginMeasure();
30077         this.textEl.setWidth(1);
30078         /*
30079          *  #2804 [new] Tabs in Roojs
30080          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30081          */
30082         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30083         //this.el.endMeasure();
30084     },
30085
30086     /**
30087      * Sets the text for the tab (Note: this also sets the tooltip text)
30088      * @param {String} text The tab's text and tooltip
30089      */
30090     setText : function(text){
30091         this.text = text;
30092         this.textEl.update(text);
30093         this.setTooltip(text);
30094         if(!this.tabPanel.resizeTabs){
30095             this.autoSize();
30096         }
30097     },
30098     /**
30099      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30100      */
30101     activate : function(){
30102         this.tabPanel.activate(this.id);
30103     },
30104
30105     /**
30106      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30107      */
30108     disable : function(){
30109         if(this.tabPanel.active != this){
30110             this.disabled = true;
30111             this.pnode.addClass("disabled");
30112         }
30113     },
30114
30115     /**
30116      * Enables this TabPanelItem if it was previously disabled.
30117      */
30118     enable : function(){
30119         this.disabled = false;
30120         this.pnode.removeClass("disabled");
30121     },
30122
30123     /**
30124      * Sets the content for this TabPanelItem.
30125      * @param {String} content The content
30126      * @param {Boolean} loadScripts true to look for and load scripts
30127      */
30128     setContent : function(content, loadScripts){
30129         this.bodyEl.update(content, loadScripts);
30130     },
30131
30132     /**
30133      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30134      * @return {Roo.UpdateManager} The UpdateManager
30135      */
30136     getUpdateManager : function(){
30137         return this.bodyEl.getUpdateManager();
30138     },
30139
30140     /**
30141      * Set a URL to be used to load the content for this TabPanelItem.
30142      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30143      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
30144      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
30145      * @return {Roo.UpdateManager} The UpdateManager
30146      */
30147     setUrl : function(url, params, loadOnce){
30148         if(this.refreshDelegate){
30149             this.un('activate', this.refreshDelegate);
30150         }
30151         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30152         this.on("activate", this.refreshDelegate);
30153         return this.bodyEl.getUpdateManager();
30154     },
30155
30156     /** @private */
30157     _handleRefresh : function(url, params, loadOnce){
30158         if(!loadOnce || !this.loaded){
30159             var updater = this.bodyEl.getUpdateManager();
30160             updater.update(url, params, this._setLoaded.createDelegate(this));
30161         }
30162     },
30163
30164     /**
30165      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30166      *   Will fail silently if the setUrl method has not been called.
30167      *   This does not activate the panel, just updates its content.
30168      */
30169     refresh : function(){
30170         if(this.refreshDelegate){
30171            this.loaded = false;
30172            this.refreshDelegate();
30173         }
30174     },
30175
30176     /** @private */
30177     _setLoaded : function(){
30178         this.loaded = true;
30179     },
30180
30181     /** @private */
30182     closeClick : function(e){
30183         var o = {};
30184         e.stopEvent();
30185         this.fireEvent("beforeclose", this, o);
30186         if(o.cancel !== true){
30187             this.tabPanel.removeTab(this.id);
30188         }
30189     },
30190     /**
30191      * The text displayed in the tooltip for the close icon.
30192      * @type String
30193      */
30194     closeText : "Close this tab"
30195 });
30196
30197 /** @private */
30198 Roo.TabPanel.prototype.createStrip = function(container){
30199     var strip = document.createElement("div");
30200     strip.className = "x-tabs-wrap";
30201     container.appendChild(strip);
30202     return strip;
30203 };
30204 /** @private */
30205 Roo.TabPanel.prototype.createStripList = function(strip){
30206     // div wrapper for retard IE
30207     // returns the "tr" element.
30208     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30209         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30210         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30211     return strip.firstChild.firstChild.firstChild.firstChild;
30212 };
30213 /** @private */
30214 Roo.TabPanel.prototype.createBody = function(container){
30215     var body = document.createElement("div");
30216     Roo.id(body, "tab-body");
30217     Roo.fly(body).addClass("x-tabs-body");
30218     container.appendChild(body);
30219     return body;
30220 };
30221 /** @private */
30222 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30223     var body = Roo.getDom(id);
30224     if(!body){
30225         body = document.createElement("div");
30226         body.id = id;
30227     }
30228     Roo.fly(body).addClass("x-tabs-item-body");
30229     bodyEl.insertBefore(body, bodyEl.firstChild);
30230     return body;
30231 };
30232 /** @private */
30233 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30234     var td = document.createElement("td");
30235     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30236     //stripEl.appendChild(td);
30237     if(closable){
30238         td.className = "x-tabs-closable";
30239         if(!this.closeTpl){
30240             this.closeTpl = new Roo.Template(
30241                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30242                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30243                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30244             );
30245         }
30246         var el = this.closeTpl.overwrite(td, {"text": text});
30247         var close = el.getElementsByTagName("div")[0];
30248         var inner = el.getElementsByTagName("em")[0];
30249         return {"el": el, "close": close, "inner": inner};
30250     } else {
30251         if(!this.tabTpl){
30252             this.tabTpl = new Roo.Template(
30253                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30254                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30255             );
30256         }
30257         var el = this.tabTpl.overwrite(td, {"text": text});
30258         var inner = el.getElementsByTagName("em")[0];
30259         return {"el": el, "inner": inner};
30260     }
30261 };/*
30262  * Based on:
30263  * Ext JS Library 1.1.1
30264  * Copyright(c) 2006-2007, Ext JS, LLC.
30265  *
30266  * Originally Released Under LGPL - original licence link has changed is not relivant.
30267  *
30268  * Fork - LGPL
30269  * <script type="text/javascript">
30270  */
30271
30272 /**
30273  * @class Roo.Button
30274  * @extends Roo.util.Observable
30275  * Simple Button class
30276  * @cfg {String} text The button text
30277  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30278  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30279  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30280  * @cfg {Object} scope The scope of the handler
30281  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30282  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30283  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30284  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30285  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30286  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30287    applies if enableToggle = true)
30288  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30289  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30290   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30291  * @constructor
30292  * Create a new button
30293  * @param {Object} config The config object
30294  */
30295 Roo.Button = function(renderTo, config)
30296 {
30297     if (!config) {
30298         config = renderTo;
30299         renderTo = config.renderTo || false;
30300     }
30301     
30302     Roo.apply(this, config);
30303     this.addEvents({
30304         /**
30305              * @event click
30306              * Fires when this button is clicked
30307              * @param {Button} this
30308              * @param {EventObject} e The click event
30309              */
30310             "click" : true,
30311         /**
30312              * @event toggle
30313              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30314              * @param {Button} this
30315              * @param {Boolean} pressed
30316              */
30317             "toggle" : true,
30318         /**
30319              * @event mouseover
30320              * Fires when the mouse hovers over the button
30321              * @param {Button} this
30322              * @param {Event} e The event object
30323              */
30324         'mouseover' : true,
30325         /**
30326              * @event mouseout
30327              * Fires when the mouse exits the button
30328              * @param {Button} this
30329              * @param {Event} e The event object
30330              */
30331         'mouseout': true,
30332          /**
30333              * @event render
30334              * Fires when the button is rendered
30335              * @param {Button} this
30336              */
30337         'render': true
30338     });
30339     if(this.menu){
30340         this.menu = Roo.menu.MenuMgr.get(this.menu);
30341     }
30342     // register listeners first!!  - so render can be captured..
30343     Roo.util.Observable.call(this);
30344     if(renderTo){
30345         this.render(renderTo);
30346     }
30347     
30348   
30349 };
30350
30351 Roo.extend(Roo.Button, Roo.util.Observable, {
30352     /**
30353      * 
30354      */
30355     
30356     /**
30357      * Read-only. True if this button is hidden
30358      * @type Boolean
30359      */
30360     hidden : false,
30361     /**
30362      * Read-only. True if this button is disabled
30363      * @type Boolean
30364      */
30365     disabled : false,
30366     /**
30367      * Read-only. True if this button is pressed (only if enableToggle = true)
30368      * @type Boolean
30369      */
30370     pressed : false,
30371
30372     /**
30373      * @cfg {Number} tabIndex 
30374      * The DOM tabIndex for this button (defaults to undefined)
30375      */
30376     tabIndex : undefined,
30377
30378     /**
30379      * @cfg {Boolean} enableToggle
30380      * True to enable pressed/not pressed toggling (defaults to false)
30381      */
30382     enableToggle: false,
30383     /**
30384      * @cfg {Roo.menu.Menu} menu
30385      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30386      */
30387     menu : undefined,
30388     /**
30389      * @cfg {String} menuAlign
30390      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30391      */
30392     menuAlign : "tl-bl?",
30393
30394     /**
30395      * @cfg {String} iconCls
30396      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30397      */
30398     iconCls : undefined,
30399     /**
30400      * @cfg {String} type
30401      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30402      */
30403     type : 'button',
30404
30405     // private
30406     menuClassTarget: 'tr',
30407
30408     /**
30409      * @cfg {String} clickEvent
30410      * The type of event to map to the button's event handler (defaults to 'click')
30411      */
30412     clickEvent : 'click',
30413
30414     /**
30415      * @cfg {Boolean} handleMouseEvents
30416      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30417      */
30418     handleMouseEvents : true,
30419
30420     /**
30421      * @cfg {String} tooltipType
30422      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30423      */
30424     tooltipType : 'qtip',
30425
30426     /**
30427      * @cfg {String} cls
30428      * A CSS class to apply to the button's main element.
30429      */
30430     
30431     /**
30432      * @cfg {Roo.Template} template (Optional)
30433      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30434      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30435      * require code modifications if required elements (e.g. a button) aren't present.
30436      */
30437
30438     // private
30439     render : function(renderTo){
30440         var btn;
30441         if(this.hideParent){
30442             this.parentEl = Roo.get(renderTo);
30443         }
30444         if(!this.dhconfig){
30445             if(!this.template){
30446                 if(!Roo.Button.buttonTemplate){
30447                     // hideous table template
30448                     Roo.Button.buttonTemplate = new Roo.Template(
30449                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30450                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
30451                         "</tr></tbody></table>");
30452                 }
30453                 this.template = Roo.Button.buttonTemplate;
30454             }
30455             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30456             var btnEl = btn.child("button:first");
30457             btnEl.on('focus', this.onFocus, this);
30458             btnEl.on('blur', this.onBlur, this);
30459             if(this.cls){
30460                 btn.addClass(this.cls);
30461             }
30462             if(this.icon){
30463                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30464             }
30465             if(this.iconCls){
30466                 btnEl.addClass(this.iconCls);
30467                 if(!this.cls){
30468                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30469                 }
30470             }
30471             if(this.tabIndex !== undefined){
30472                 btnEl.dom.tabIndex = this.tabIndex;
30473             }
30474             if(this.tooltip){
30475                 if(typeof this.tooltip == 'object'){
30476                     Roo.QuickTips.tips(Roo.apply({
30477                           target: btnEl.id
30478                     }, this.tooltip));
30479                 } else {
30480                     btnEl.dom[this.tooltipType] = this.tooltip;
30481                 }
30482             }
30483         }else{
30484             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30485         }
30486         this.el = btn;
30487         if(this.id){
30488             this.el.dom.id = this.el.id = this.id;
30489         }
30490         if(this.menu){
30491             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30492             this.menu.on("show", this.onMenuShow, this);
30493             this.menu.on("hide", this.onMenuHide, this);
30494         }
30495         btn.addClass("x-btn");
30496         if(Roo.isIE && !Roo.isIE7){
30497             this.autoWidth.defer(1, this);
30498         }else{
30499             this.autoWidth();
30500         }
30501         if(this.handleMouseEvents){
30502             btn.on("mouseover", this.onMouseOver, this);
30503             btn.on("mouseout", this.onMouseOut, this);
30504             btn.on("mousedown", this.onMouseDown, this);
30505         }
30506         btn.on(this.clickEvent, this.onClick, this);
30507         //btn.on("mouseup", this.onMouseUp, this);
30508         if(this.hidden){
30509             this.hide();
30510         }
30511         if(this.disabled){
30512             this.disable();
30513         }
30514         Roo.ButtonToggleMgr.register(this);
30515         if(this.pressed){
30516             this.el.addClass("x-btn-pressed");
30517         }
30518         if(this.repeat){
30519             var repeater = new Roo.util.ClickRepeater(btn,
30520                 typeof this.repeat == "object" ? this.repeat : {}
30521             );
30522             repeater.on("click", this.onClick,  this);
30523         }
30524         
30525         this.fireEvent('render', this);
30526         
30527     },
30528     /**
30529      * Returns the button's underlying element
30530      * @return {Roo.Element} The element
30531      */
30532     getEl : function(){
30533         return this.el;  
30534     },
30535     
30536     /**
30537      * Destroys this Button and removes any listeners.
30538      */
30539     destroy : function(){
30540         Roo.ButtonToggleMgr.unregister(this);
30541         this.el.removeAllListeners();
30542         this.purgeListeners();
30543         this.el.remove();
30544     },
30545
30546     // private
30547     autoWidth : function(){
30548         if(this.el){
30549             this.el.setWidth("auto");
30550             if(Roo.isIE7 && Roo.isStrict){
30551                 var ib = this.el.child('button');
30552                 if(ib && ib.getWidth() > 20){
30553                     ib.clip();
30554                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30555                 }
30556             }
30557             if(this.minWidth){
30558                 if(this.hidden){
30559                     this.el.beginMeasure();
30560                 }
30561                 if(this.el.getWidth() < this.minWidth){
30562                     this.el.setWidth(this.minWidth);
30563                 }
30564                 if(this.hidden){
30565                     this.el.endMeasure();
30566                 }
30567             }
30568         }
30569     },
30570
30571     /**
30572      * Assigns this button's click handler
30573      * @param {Function} handler The function to call when the button is clicked
30574      * @param {Object} scope (optional) Scope for the function passed in
30575      */
30576     setHandler : function(handler, scope){
30577         this.handler = handler;
30578         this.scope = scope;  
30579     },
30580     
30581     /**
30582      * Sets this button's text
30583      * @param {String} text The button text
30584      */
30585     setText : function(text){
30586         this.text = text;
30587         if(this.el){
30588             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30589         }
30590         this.autoWidth();
30591     },
30592     
30593     /**
30594      * Gets the text for this button
30595      * @return {String} The button text
30596      */
30597     getText : function(){
30598         return this.text;  
30599     },
30600     
30601     /**
30602      * Show this button
30603      */
30604     show: function(){
30605         this.hidden = false;
30606         if(this.el){
30607             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30608         }
30609     },
30610     
30611     /**
30612      * Hide this button
30613      */
30614     hide: function(){
30615         this.hidden = true;
30616         if(this.el){
30617             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30618         }
30619     },
30620     
30621     /**
30622      * Convenience function for boolean show/hide
30623      * @param {Boolean} visible True to show, false to hide
30624      */
30625     setVisible: function(visible){
30626         if(visible) {
30627             this.show();
30628         }else{
30629             this.hide();
30630         }
30631     },
30632     
30633     /**
30634      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30635      * @param {Boolean} state (optional) Force a particular state
30636      */
30637     toggle : function(state){
30638         state = state === undefined ? !this.pressed : state;
30639         if(state != this.pressed){
30640             if(state){
30641                 this.el.addClass("x-btn-pressed");
30642                 this.pressed = true;
30643                 this.fireEvent("toggle", this, true);
30644             }else{
30645                 this.el.removeClass("x-btn-pressed");
30646                 this.pressed = false;
30647                 this.fireEvent("toggle", this, false);
30648             }
30649             if(this.toggleHandler){
30650                 this.toggleHandler.call(this.scope || this, this, state);
30651             }
30652         }
30653     },
30654     
30655     /**
30656      * Focus the button
30657      */
30658     focus : function(){
30659         this.el.child('button:first').focus();
30660     },
30661     
30662     /**
30663      * Disable this button
30664      */
30665     disable : function(){
30666         if(this.el){
30667             this.el.addClass("x-btn-disabled");
30668         }
30669         this.disabled = true;
30670     },
30671     
30672     /**
30673      * Enable this button
30674      */
30675     enable : function(){
30676         if(this.el){
30677             this.el.removeClass("x-btn-disabled");
30678         }
30679         this.disabled = false;
30680     },
30681
30682     /**
30683      * Convenience function for boolean enable/disable
30684      * @param {Boolean} enabled True to enable, false to disable
30685      */
30686     setDisabled : function(v){
30687         this[v !== true ? "enable" : "disable"]();
30688     },
30689
30690     // private
30691     onClick : function(e)
30692     {
30693         if(e){
30694             e.preventDefault();
30695         }
30696         if(e.button != 0){
30697             return;
30698         }
30699         if(!this.disabled){
30700             if(this.enableToggle){
30701                 this.toggle();
30702             }
30703             if(this.menu && !this.menu.isVisible()){
30704                 this.menu.show(this.el, this.menuAlign);
30705             }
30706             this.fireEvent("click", this, e);
30707             if(this.handler){
30708                 this.el.removeClass("x-btn-over");
30709                 this.handler.call(this.scope || this, this, e);
30710             }
30711         }
30712     },
30713     // private
30714     onMouseOver : function(e){
30715         if(!this.disabled){
30716             this.el.addClass("x-btn-over");
30717             this.fireEvent('mouseover', this, e);
30718         }
30719     },
30720     // private
30721     onMouseOut : function(e){
30722         if(!e.within(this.el,  true)){
30723             this.el.removeClass("x-btn-over");
30724             this.fireEvent('mouseout', this, e);
30725         }
30726     },
30727     // private
30728     onFocus : function(e){
30729         if(!this.disabled){
30730             this.el.addClass("x-btn-focus");
30731         }
30732     },
30733     // private
30734     onBlur : function(e){
30735         this.el.removeClass("x-btn-focus");
30736     },
30737     // private
30738     onMouseDown : function(e){
30739         if(!this.disabled && e.button == 0){
30740             this.el.addClass("x-btn-click");
30741             Roo.get(document).on('mouseup', this.onMouseUp, this);
30742         }
30743     },
30744     // private
30745     onMouseUp : function(e){
30746         if(e.button == 0){
30747             this.el.removeClass("x-btn-click");
30748             Roo.get(document).un('mouseup', this.onMouseUp, this);
30749         }
30750     },
30751     // private
30752     onMenuShow : function(e){
30753         this.el.addClass("x-btn-menu-active");
30754     },
30755     // private
30756     onMenuHide : function(e){
30757         this.el.removeClass("x-btn-menu-active");
30758     }   
30759 });
30760
30761 // Private utility class used by Button
30762 Roo.ButtonToggleMgr = function(){
30763    var groups = {};
30764    
30765    function toggleGroup(btn, state){
30766        if(state){
30767            var g = groups[btn.toggleGroup];
30768            for(var i = 0, l = g.length; i < l; i++){
30769                if(g[i] != btn){
30770                    g[i].toggle(false);
30771                }
30772            }
30773        }
30774    }
30775    
30776    return {
30777        register : function(btn){
30778            if(!btn.toggleGroup){
30779                return;
30780            }
30781            var g = groups[btn.toggleGroup];
30782            if(!g){
30783                g = groups[btn.toggleGroup] = [];
30784            }
30785            g.push(btn);
30786            btn.on("toggle", toggleGroup);
30787        },
30788        
30789        unregister : function(btn){
30790            if(!btn.toggleGroup){
30791                return;
30792            }
30793            var g = groups[btn.toggleGroup];
30794            if(g){
30795                g.remove(btn);
30796                btn.un("toggle", toggleGroup);
30797            }
30798        }
30799    };
30800 }();/*
30801  * Based on:
30802  * Ext JS Library 1.1.1
30803  * Copyright(c) 2006-2007, Ext JS, LLC.
30804  *
30805  * Originally Released Under LGPL - original licence link has changed is not relivant.
30806  *
30807  * Fork - LGPL
30808  * <script type="text/javascript">
30809  */
30810  
30811 /**
30812  * @class Roo.SplitButton
30813  * @extends Roo.Button
30814  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
30815  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
30816  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
30817  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
30818  * @cfg {String} arrowTooltip The title attribute of the arrow
30819  * @constructor
30820  * Create a new menu button
30821  * @param {String/HTMLElement/Element} renderTo The element to append the button to
30822  * @param {Object} config The config object
30823  */
30824 Roo.SplitButton = function(renderTo, config){
30825     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
30826     /**
30827      * @event arrowclick
30828      * Fires when this button's arrow is clicked
30829      * @param {SplitButton} this
30830      * @param {EventObject} e The click event
30831      */
30832     this.addEvents({"arrowclick":true});
30833 };
30834
30835 Roo.extend(Roo.SplitButton, Roo.Button, {
30836     render : function(renderTo){
30837         // this is one sweet looking template!
30838         var tpl = new Roo.Template(
30839             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
30840             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
30841             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
30842             "</tbody></table></td><td>",
30843             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
30844             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
30845             "</tbody></table></td></tr></table>"
30846         );
30847         var btn = tpl.append(renderTo, [this.text, this.type], true);
30848         var btnEl = btn.child("button");
30849         if(this.cls){
30850             btn.addClass(this.cls);
30851         }
30852         if(this.icon){
30853             btnEl.setStyle('background-image', 'url(' +this.icon +')');
30854         }
30855         if(this.iconCls){
30856             btnEl.addClass(this.iconCls);
30857             if(!this.cls){
30858                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30859             }
30860         }
30861         this.el = btn;
30862         if(this.handleMouseEvents){
30863             btn.on("mouseover", this.onMouseOver, this);
30864             btn.on("mouseout", this.onMouseOut, this);
30865             btn.on("mousedown", this.onMouseDown, this);
30866             btn.on("mouseup", this.onMouseUp, this);
30867         }
30868         btn.on(this.clickEvent, this.onClick, this);
30869         if(this.tooltip){
30870             if(typeof this.tooltip == 'object'){
30871                 Roo.QuickTips.tips(Roo.apply({
30872                       target: btnEl.id
30873                 }, this.tooltip));
30874             } else {
30875                 btnEl.dom[this.tooltipType] = this.tooltip;
30876             }
30877         }
30878         if(this.arrowTooltip){
30879             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30880         }
30881         if(this.hidden){
30882             this.hide();
30883         }
30884         if(this.disabled){
30885             this.disable();
30886         }
30887         if(this.pressed){
30888             this.el.addClass("x-btn-pressed");
30889         }
30890         if(Roo.isIE && !Roo.isIE7){
30891             this.autoWidth.defer(1, this);
30892         }else{
30893             this.autoWidth();
30894         }
30895         if(this.menu){
30896             this.menu.on("show", this.onMenuShow, this);
30897             this.menu.on("hide", this.onMenuHide, this);
30898         }
30899         this.fireEvent('render', this);
30900     },
30901
30902     // private
30903     autoWidth : function(){
30904         if(this.el){
30905             var tbl = this.el.child("table:first");
30906             var tbl2 = this.el.child("table:last");
30907             this.el.setWidth("auto");
30908             tbl.setWidth("auto");
30909             if(Roo.isIE7 && Roo.isStrict){
30910                 var ib = this.el.child('button:first');
30911                 if(ib && ib.getWidth() > 20){
30912                     ib.clip();
30913                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30914                 }
30915             }
30916             if(this.minWidth){
30917                 if(this.hidden){
30918                     this.el.beginMeasure();
30919                 }
30920                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30921                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30922                 }
30923                 if(this.hidden){
30924                     this.el.endMeasure();
30925                 }
30926             }
30927             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30928         } 
30929     },
30930     /**
30931      * Sets this button's click handler
30932      * @param {Function} handler The function to call when the button is clicked
30933      * @param {Object} scope (optional) Scope for the function passed above
30934      */
30935     setHandler : function(handler, scope){
30936         this.handler = handler;
30937         this.scope = scope;  
30938     },
30939     
30940     /**
30941      * Sets this button's arrow click handler
30942      * @param {Function} handler The function to call when the arrow is clicked
30943      * @param {Object} scope (optional) Scope for the function passed above
30944      */
30945     setArrowHandler : function(handler, scope){
30946         this.arrowHandler = handler;
30947         this.scope = scope;  
30948     },
30949     
30950     /**
30951      * Focus the button
30952      */
30953     focus : function(){
30954         if(this.el){
30955             this.el.child("button:first").focus();
30956         }
30957     },
30958
30959     // private
30960     onClick : function(e){
30961         e.preventDefault();
30962         if(!this.disabled){
30963             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30964                 if(this.menu && !this.menu.isVisible()){
30965                     this.menu.show(this.el, this.menuAlign);
30966                 }
30967                 this.fireEvent("arrowclick", this, e);
30968                 if(this.arrowHandler){
30969                     this.arrowHandler.call(this.scope || this, this, e);
30970                 }
30971             }else{
30972                 this.fireEvent("click", this, e);
30973                 if(this.handler){
30974                     this.handler.call(this.scope || this, this, e);
30975                 }
30976             }
30977         }
30978     },
30979     // private
30980     onMouseDown : function(e){
30981         if(!this.disabled){
30982             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30983         }
30984     },
30985     // private
30986     onMouseUp : function(e){
30987         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30988     }   
30989 });
30990
30991
30992 // backwards compat
30993 Roo.MenuButton = Roo.SplitButton;/*
30994  * Based on:
30995  * Ext JS Library 1.1.1
30996  * Copyright(c) 2006-2007, Ext JS, LLC.
30997  *
30998  * Originally Released Under LGPL - original licence link has changed is not relivant.
30999  *
31000  * Fork - LGPL
31001  * <script type="text/javascript">
31002  */
31003
31004 /**
31005  * @class Roo.Toolbar
31006  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
31007  * Basic Toolbar class.
31008  * @constructor
31009  * Creates a new Toolbar
31010  * @param {Object} container The config object
31011  */ 
31012 Roo.Toolbar = function(container, buttons, config)
31013 {
31014     /// old consturctor format still supported..
31015     if(container instanceof Array){ // omit the container for later rendering
31016         buttons = container;
31017         config = buttons;
31018         container = null;
31019     }
31020     if (typeof(container) == 'object' && container.xtype) {
31021         config = container;
31022         container = config.container;
31023         buttons = config.buttons || []; // not really - use items!!
31024     }
31025     var xitems = [];
31026     if (config && config.items) {
31027         xitems = config.items;
31028         delete config.items;
31029     }
31030     Roo.apply(this, config);
31031     this.buttons = buttons;
31032     
31033     if(container){
31034         this.render(container);
31035     }
31036     this.xitems = xitems;
31037     Roo.each(xitems, function(b) {
31038         this.add(b);
31039     }, this);
31040     
31041 };
31042
31043 Roo.Toolbar.prototype = {
31044     /**
31045      * @cfg {Array} items
31046      * array of button configs or elements to add (will be converted to a MixedCollection)
31047      */
31048     items: false,
31049     /**
31050      * @cfg {String/HTMLElement/Element} container
31051      * The id or element that will contain the toolbar
31052      */
31053     // private
31054     render : function(ct){
31055         this.el = Roo.get(ct);
31056         if(this.cls){
31057             this.el.addClass(this.cls);
31058         }
31059         // using a table allows for vertical alignment
31060         // 100% width is needed by Safari...
31061         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31062         this.tr = this.el.child("tr", true);
31063         var autoId = 0;
31064         this.items = new Roo.util.MixedCollection(false, function(o){
31065             return o.id || ("item" + (++autoId));
31066         });
31067         if(this.buttons){
31068             this.add.apply(this, this.buttons);
31069             delete this.buttons;
31070         }
31071     },
31072
31073     /**
31074      * Adds element(s) to the toolbar -- this function takes a variable number of 
31075      * arguments of mixed type and adds them to the toolbar.
31076      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31077      * <ul>
31078      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31079      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31080      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31081      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31082      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31083      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31084      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31085      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31086      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31087      * </ul>
31088      * @param {Mixed} arg2
31089      * @param {Mixed} etc.
31090      */
31091     add : function(){
31092         var a = arguments, l = a.length;
31093         for(var i = 0; i < l; i++){
31094             this._add(a[i]);
31095         }
31096     },
31097     // private..
31098     _add : function(el) {
31099         
31100         if (el.xtype) {
31101             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31102         }
31103         
31104         if (el.applyTo){ // some kind of form field
31105             return this.addField(el);
31106         } 
31107         if (el.render){ // some kind of Toolbar.Item
31108             return this.addItem(el);
31109         }
31110         if (typeof el == "string"){ // string
31111             if(el == "separator" || el == "-"){
31112                 return this.addSeparator();
31113             }
31114             if (el == " "){
31115                 return this.addSpacer();
31116             }
31117             if(el == "->"){
31118                 return this.addFill();
31119             }
31120             return this.addText(el);
31121             
31122         }
31123         if(el.tagName){ // element
31124             return this.addElement(el);
31125         }
31126         if(typeof el == "object"){ // must be button config?
31127             return this.addButton(el);
31128         }
31129         // and now what?!?!
31130         return false;
31131         
31132     },
31133     
31134     /**
31135      * Add an Xtype element
31136      * @param {Object} xtype Xtype Object
31137      * @return {Object} created Object
31138      */
31139     addxtype : function(e){
31140         return this.add(e);  
31141     },
31142     
31143     /**
31144      * Returns the Element for this toolbar.
31145      * @return {Roo.Element}
31146      */
31147     getEl : function(){
31148         return this.el;  
31149     },
31150     
31151     /**
31152      * Adds a separator
31153      * @return {Roo.Toolbar.Item} The separator item
31154      */
31155     addSeparator : function(){
31156         return this.addItem(new Roo.Toolbar.Separator());
31157     },
31158
31159     /**
31160      * Adds a spacer element
31161      * @return {Roo.Toolbar.Spacer} The spacer item
31162      */
31163     addSpacer : function(){
31164         return this.addItem(new Roo.Toolbar.Spacer());
31165     },
31166
31167     /**
31168      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31169      * @return {Roo.Toolbar.Fill} The fill item
31170      */
31171     addFill : function(){
31172         return this.addItem(new Roo.Toolbar.Fill());
31173     },
31174
31175     /**
31176      * Adds any standard HTML element to the toolbar
31177      * @param {String/HTMLElement/Element} el The element or id of the element to add
31178      * @return {Roo.Toolbar.Item} The element's item
31179      */
31180     addElement : function(el){
31181         return this.addItem(new Roo.Toolbar.Item(el));
31182     },
31183     /**
31184      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31185      * @type Roo.util.MixedCollection  
31186      */
31187     items : false,
31188      
31189     /**
31190      * Adds any Toolbar.Item or subclass
31191      * @param {Roo.Toolbar.Item} item
31192      * @return {Roo.Toolbar.Item} The item
31193      */
31194     addItem : function(item){
31195         var td = this.nextBlock();
31196         item.render(td);
31197         this.items.add(item);
31198         return item;
31199     },
31200     
31201     /**
31202      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31203      * @param {Object/Array} config A button config or array of configs
31204      * @return {Roo.Toolbar.Button/Array}
31205      */
31206     addButton : function(config){
31207         if(config instanceof Array){
31208             var buttons = [];
31209             for(var i = 0, len = config.length; i < len; i++) {
31210                 buttons.push(this.addButton(config[i]));
31211             }
31212             return buttons;
31213         }
31214         var b = config;
31215         if(!(config instanceof Roo.Toolbar.Button)){
31216             b = config.split ?
31217                 new Roo.Toolbar.SplitButton(config) :
31218                 new Roo.Toolbar.Button(config);
31219         }
31220         var td = this.nextBlock();
31221         b.render(td);
31222         this.items.add(b);
31223         return b;
31224     },
31225     
31226     /**
31227      * Adds text to the toolbar
31228      * @param {String} text The text to add
31229      * @return {Roo.Toolbar.Item} The element's item
31230      */
31231     addText : function(text){
31232         return this.addItem(new Roo.Toolbar.TextItem(text));
31233     },
31234     
31235     /**
31236      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31237      * @param {Number} index The index where the item is to be inserted
31238      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31239      * @return {Roo.Toolbar.Button/Item}
31240      */
31241     insertButton : function(index, item){
31242         if(item instanceof Array){
31243             var buttons = [];
31244             for(var i = 0, len = item.length; i < len; i++) {
31245                buttons.push(this.insertButton(index + i, item[i]));
31246             }
31247             return buttons;
31248         }
31249         if (!(item instanceof Roo.Toolbar.Button)){
31250            item = new Roo.Toolbar.Button(item);
31251         }
31252         var td = document.createElement("td");
31253         this.tr.insertBefore(td, this.tr.childNodes[index]);
31254         item.render(td);
31255         this.items.insert(index, item);
31256         return item;
31257     },
31258     
31259     /**
31260      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31261      * @param {Object} config
31262      * @return {Roo.Toolbar.Item} The element's item
31263      */
31264     addDom : function(config, returnEl){
31265         var td = this.nextBlock();
31266         Roo.DomHelper.overwrite(td, config);
31267         var ti = new Roo.Toolbar.Item(td.firstChild);
31268         ti.render(td);
31269         this.items.add(ti);
31270         return ti;
31271     },
31272
31273     /**
31274      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31275      * @type Roo.util.MixedCollection  
31276      */
31277     fields : false,
31278     
31279     /**
31280      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31281      * Note: the field should not have been rendered yet. For a field that has already been
31282      * rendered, use {@link #addElement}.
31283      * @param {Roo.form.Field} field
31284      * @return {Roo.ToolbarItem}
31285      */
31286      
31287       
31288     addField : function(field) {
31289         if (!this.fields) {
31290             var autoId = 0;
31291             this.fields = new Roo.util.MixedCollection(false, function(o){
31292                 return o.id || ("item" + (++autoId));
31293             });
31294
31295         }
31296         
31297         var td = this.nextBlock();
31298         field.render(td);
31299         var ti = new Roo.Toolbar.Item(td.firstChild);
31300         ti.render(td);
31301         this.items.add(ti);
31302         this.fields.add(field);
31303         return ti;
31304     },
31305     /**
31306      * Hide the toolbar
31307      * @method hide
31308      */
31309      
31310       
31311     hide : function()
31312     {
31313         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31314         this.el.child('div').hide();
31315     },
31316     /**
31317      * Show the toolbar
31318      * @method show
31319      */
31320     show : function()
31321     {
31322         this.el.child('div').show();
31323     },
31324       
31325     // private
31326     nextBlock : function(){
31327         var td = document.createElement("td");
31328         this.tr.appendChild(td);
31329         return td;
31330     },
31331
31332     // private
31333     destroy : function(){
31334         if(this.items){ // rendered?
31335             Roo.destroy.apply(Roo, this.items.items);
31336         }
31337         if(this.fields){ // rendered?
31338             Roo.destroy.apply(Roo, this.fields.items);
31339         }
31340         Roo.Element.uncache(this.el, this.tr);
31341     }
31342 };
31343
31344 /**
31345  * @class Roo.Toolbar.Item
31346  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31347  * @constructor
31348  * Creates a new Item
31349  * @param {HTMLElement} el 
31350  */
31351 Roo.Toolbar.Item = function(el){
31352     var cfg = {};
31353     if (typeof (el.xtype) != 'undefined') {
31354         cfg = el;
31355         el = cfg.el;
31356     }
31357     
31358     this.el = Roo.getDom(el);
31359     this.id = Roo.id(this.el);
31360     this.hidden = false;
31361     
31362     this.addEvents({
31363          /**
31364              * @event render
31365              * Fires when the button is rendered
31366              * @param {Button} this
31367              */
31368         'render': true
31369     });
31370     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31371 };
31372 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31373 //Roo.Toolbar.Item.prototype = {
31374     
31375     /**
31376      * Get this item's HTML Element
31377      * @return {HTMLElement}
31378      */
31379     getEl : function(){
31380        return this.el;  
31381     },
31382
31383     // private
31384     render : function(td){
31385         
31386          this.td = td;
31387         td.appendChild(this.el);
31388         
31389         this.fireEvent('render', this);
31390     },
31391     
31392     /**
31393      * Removes and destroys this item.
31394      */
31395     destroy : function(){
31396         this.td.parentNode.removeChild(this.td);
31397     },
31398     
31399     /**
31400      * Shows this item.
31401      */
31402     show: function(){
31403         this.hidden = false;
31404         this.td.style.display = "";
31405     },
31406     
31407     /**
31408      * Hides this item.
31409      */
31410     hide: function(){
31411         this.hidden = true;
31412         this.td.style.display = "none";
31413     },
31414     
31415     /**
31416      * Convenience function for boolean show/hide.
31417      * @param {Boolean} visible true to show/false to hide
31418      */
31419     setVisible: function(visible){
31420         if(visible) {
31421             this.show();
31422         }else{
31423             this.hide();
31424         }
31425     },
31426     
31427     /**
31428      * Try to focus this item.
31429      */
31430     focus : function(){
31431         Roo.fly(this.el).focus();
31432     },
31433     
31434     /**
31435      * Disables this item.
31436      */
31437     disable : function(){
31438         Roo.fly(this.td).addClass("x-item-disabled");
31439         this.disabled = true;
31440         this.el.disabled = true;
31441     },
31442     
31443     /**
31444      * Enables this item.
31445      */
31446     enable : function(){
31447         Roo.fly(this.td).removeClass("x-item-disabled");
31448         this.disabled = false;
31449         this.el.disabled = false;
31450     }
31451 });
31452
31453
31454 /**
31455  * @class Roo.Toolbar.Separator
31456  * @extends Roo.Toolbar.Item
31457  * A simple toolbar separator class
31458  * @constructor
31459  * Creates a new Separator
31460  */
31461 Roo.Toolbar.Separator = function(cfg){
31462     
31463     var s = document.createElement("span");
31464     s.className = "ytb-sep";
31465     if (cfg) {
31466         cfg.el = s;
31467     }
31468     
31469     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31470 };
31471 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31472     enable:Roo.emptyFn,
31473     disable:Roo.emptyFn,
31474     focus:Roo.emptyFn
31475 });
31476
31477 /**
31478  * @class Roo.Toolbar.Spacer
31479  * @extends Roo.Toolbar.Item
31480  * A simple element that adds extra horizontal space to a toolbar.
31481  * @constructor
31482  * Creates a new Spacer
31483  */
31484 Roo.Toolbar.Spacer = function(cfg){
31485     var s = document.createElement("div");
31486     s.className = "ytb-spacer";
31487     if (cfg) {
31488         cfg.el = s;
31489     }
31490     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31491 };
31492 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31493     enable:Roo.emptyFn,
31494     disable:Roo.emptyFn,
31495     focus:Roo.emptyFn
31496 });
31497
31498 /**
31499  * @class Roo.Toolbar.Fill
31500  * @extends Roo.Toolbar.Spacer
31501  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31502  * @constructor
31503  * Creates a new Spacer
31504  */
31505 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31506     // private
31507     render : function(td){
31508         td.style.width = '100%';
31509         Roo.Toolbar.Fill.superclass.render.call(this, td);
31510     }
31511 });
31512
31513 /**
31514  * @class Roo.Toolbar.TextItem
31515  * @extends Roo.Toolbar.Item
31516  * A simple class that renders text directly into a toolbar.
31517  * @constructor
31518  * Creates a new TextItem
31519  * @cfg {string} text 
31520  */
31521 Roo.Toolbar.TextItem = function(cfg){
31522     var  text = cfg || "";
31523     if (typeof(cfg) == 'object') {
31524         text = cfg.text || "";
31525     }  else {
31526         cfg = null;
31527     }
31528     var s = document.createElement("span");
31529     s.className = "ytb-text";
31530     s.innerHTML = text;
31531     if (cfg) {
31532         cfg.el  = s;
31533     }
31534     
31535     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31536 };
31537 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31538     
31539      
31540     enable:Roo.emptyFn,
31541     disable:Roo.emptyFn,
31542     focus:Roo.emptyFn,
31543      /**
31544      * Shows this button
31545      */
31546     show: function(){
31547         this.hidden = false;
31548         this.el.style.display = "";
31549     },
31550     
31551     /**
31552      * Hides this button
31553      */
31554     hide: function(){
31555         this.hidden = true;
31556         this.el.style.display = "none";
31557     }
31558     
31559 });
31560
31561 /**
31562  * @class Roo.Toolbar.Button
31563  * @extends Roo.Button
31564  * A button that renders into a toolbar.
31565  * @constructor
31566  * Creates a new Button
31567  * @param {Object} config A standard {@link Roo.Button} config object
31568  */
31569 Roo.Toolbar.Button = function(config){
31570     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31571 };
31572 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31573 {
31574     
31575     
31576     render : function(td){
31577         this.td = td;
31578         Roo.Toolbar.Button.superclass.render.call(this, td);
31579     },
31580     
31581     /**
31582      * Removes and destroys this button
31583      */
31584     destroy : function(){
31585         Roo.Toolbar.Button.superclass.destroy.call(this);
31586         this.td.parentNode.removeChild(this.td);
31587     },
31588     
31589     /**
31590      * Shows this button
31591      */
31592     show: function(){
31593         this.hidden = false;
31594         this.td.style.display = "";
31595     },
31596     
31597     /**
31598      * Hides this button
31599      */
31600     hide: function(){
31601         this.hidden = true;
31602         this.td.style.display = "none";
31603     },
31604
31605     /**
31606      * Disables this item
31607      */
31608     disable : function(){
31609         Roo.fly(this.td).addClass("x-item-disabled");
31610         this.disabled = true;
31611     },
31612
31613     /**
31614      * Enables this item
31615      */
31616     enable : function(){
31617         Roo.fly(this.td).removeClass("x-item-disabled");
31618         this.disabled = false;
31619     }
31620 });
31621 // backwards compat
31622 Roo.ToolbarButton = Roo.Toolbar.Button;
31623
31624 /**
31625  * @class Roo.Toolbar.SplitButton
31626  * @extends Roo.SplitButton
31627  * A menu button that renders into a toolbar.
31628  * @constructor
31629  * Creates a new SplitButton
31630  * @param {Object} config A standard {@link Roo.SplitButton} config object
31631  */
31632 Roo.Toolbar.SplitButton = function(config){
31633     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31634 };
31635 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31636     render : function(td){
31637         this.td = td;
31638         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31639     },
31640     
31641     /**
31642      * Removes and destroys this button
31643      */
31644     destroy : function(){
31645         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31646         this.td.parentNode.removeChild(this.td);
31647     },
31648     
31649     /**
31650      * Shows this button
31651      */
31652     show: function(){
31653         this.hidden = false;
31654         this.td.style.display = "";
31655     },
31656     
31657     /**
31658      * Hides this button
31659      */
31660     hide: function(){
31661         this.hidden = true;
31662         this.td.style.display = "none";
31663     }
31664 });
31665
31666 // backwards compat
31667 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31668  * Based on:
31669  * Ext JS Library 1.1.1
31670  * Copyright(c) 2006-2007, Ext JS, LLC.
31671  *
31672  * Originally Released Under LGPL - original licence link has changed is not relivant.
31673  *
31674  * Fork - LGPL
31675  * <script type="text/javascript">
31676  */
31677  
31678 /**
31679  * @class Roo.PagingToolbar
31680  * @extends Roo.Toolbar
31681  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
31682  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31683  * @constructor
31684  * Create a new PagingToolbar
31685  * @param {Object} config The config object
31686  */
31687 Roo.PagingToolbar = function(el, ds, config)
31688 {
31689     // old args format still supported... - xtype is prefered..
31690     if (typeof(el) == 'object' && el.xtype) {
31691         // created from xtype...
31692         config = el;
31693         ds = el.dataSource;
31694         el = config.container;
31695     }
31696     var items = [];
31697     if (config.items) {
31698         items = config.items;
31699         config.items = [];
31700     }
31701     
31702     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
31703     this.ds = ds;
31704     this.cursor = 0;
31705     this.renderButtons(this.el);
31706     this.bind(ds);
31707     
31708     // supprot items array.
31709    
31710     Roo.each(items, function(e) {
31711         this.add(Roo.factory(e));
31712     },this);
31713     
31714 };
31715
31716 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
31717    
31718     /**
31719      * @cfg {String/HTMLElement/Element} container
31720      * container The id or element that will contain the toolbar
31721      */
31722     /**
31723      * @cfg {Boolean} displayInfo
31724      * True to display the displayMsg (defaults to false)
31725      */
31726     
31727     
31728     /**
31729      * @cfg {Number} pageSize
31730      * The number of records to display per page (defaults to 20)
31731      */
31732     pageSize: 20,
31733     /**
31734      * @cfg {String} displayMsg
31735      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31736      */
31737     displayMsg : 'Displaying {0} - {1} of {2}',
31738     /**
31739      * @cfg {String} emptyMsg
31740      * The message to display when no records are found (defaults to "No data to display")
31741      */
31742     emptyMsg : 'No data to display',
31743     /**
31744      * Customizable piece of the default paging text (defaults to "Page")
31745      * @type String
31746      */
31747     beforePageText : "Page",
31748     /**
31749      * Customizable piece of the default paging text (defaults to "of %0")
31750      * @type String
31751      */
31752     afterPageText : "of {0}",
31753     /**
31754      * Customizable piece of the default paging text (defaults to "First Page")
31755      * @type String
31756      */
31757     firstText : "First Page",
31758     /**
31759      * Customizable piece of the default paging text (defaults to "Previous Page")
31760      * @type String
31761      */
31762     prevText : "Previous Page",
31763     /**
31764      * Customizable piece of the default paging text (defaults to "Next Page")
31765      * @type String
31766      */
31767     nextText : "Next Page",
31768     /**
31769      * Customizable piece of the default paging text (defaults to "Last Page")
31770      * @type String
31771      */
31772     lastText : "Last Page",
31773     /**
31774      * Customizable piece of the default paging text (defaults to "Refresh")
31775      * @type String
31776      */
31777     refreshText : "Refresh",
31778
31779     // private
31780     renderButtons : function(el){
31781         Roo.PagingToolbar.superclass.render.call(this, el);
31782         this.first = this.addButton({
31783             tooltip: this.firstText,
31784             cls: "x-btn-icon x-grid-page-first",
31785             disabled: true,
31786             handler: this.onClick.createDelegate(this, ["first"])
31787         });
31788         this.prev = this.addButton({
31789             tooltip: this.prevText,
31790             cls: "x-btn-icon x-grid-page-prev",
31791             disabled: true,
31792             handler: this.onClick.createDelegate(this, ["prev"])
31793         });
31794         //this.addSeparator();
31795         this.add(this.beforePageText);
31796         this.field = Roo.get(this.addDom({
31797            tag: "input",
31798            type: "text",
31799            size: "3",
31800            value: "1",
31801            cls: "x-grid-page-number"
31802         }).el);
31803         this.field.on("keydown", this.onPagingKeydown, this);
31804         this.field.on("focus", function(){this.dom.select();});
31805         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
31806         this.field.setHeight(18);
31807         //this.addSeparator();
31808         this.next = this.addButton({
31809             tooltip: this.nextText,
31810             cls: "x-btn-icon x-grid-page-next",
31811             disabled: true,
31812             handler: this.onClick.createDelegate(this, ["next"])
31813         });
31814         this.last = this.addButton({
31815             tooltip: this.lastText,
31816             cls: "x-btn-icon x-grid-page-last",
31817             disabled: true,
31818             handler: this.onClick.createDelegate(this, ["last"])
31819         });
31820         //this.addSeparator();
31821         this.loading = this.addButton({
31822             tooltip: this.refreshText,
31823             cls: "x-btn-icon x-grid-loading",
31824             handler: this.onClick.createDelegate(this, ["refresh"])
31825         });
31826
31827         if(this.displayInfo){
31828             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
31829         }
31830     },
31831
31832     // private
31833     updateInfo : function(){
31834         if(this.displayEl){
31835             var count = this.ds.getCount();
31836             var msg = count == 0 ?
31837                 this.emptyMsg :
31838                 String.format(
31839                     this.displayMsg,
31840                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31841                 );
31842             this.displayEl.update(msg);
31843         }
31844     },
31845
31846     // private
31847     onLoad : function(ds, r, o){
31848        this.cursor = o.params ? o.params.start : 0;
31849        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
31850
31851        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
31852        this.field.dom.value = ap;
31853        this.first.setDisabled(ap == 1);
31854        this.prev.setDisabled(ap == 1);
31855        this.next.setDisabled(ap == ps);
31856        this.last.setDisabled(ap == ps);
31857        this.loading.enable();
31858        this.updateInfo();
31859     },
31860
31861     // private
31862     getPageData : function(){
31863         var total = this.ds.getTotalCount();
31864         return {
31865             total : total,
31866             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31867             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31868         };
31869     },
31870
31871     // private
31872     onLoadError : function(){
31873         this.loading.enable();
31874     },
31875
31876     // private
31877     onPagingKeydown : function(e){
31878         var k = e.getKey();
31879         var d = this.getPageData();
31880         if(k == e.RETURN){
31881             var v = this.field.dom.value, pageNum;
31882             if(!v || isNaN(pageNum = parseInt(v, 10))){
31883                 this.field.dom.value = d.activePage;
31884                 return;
31885             }
31886             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31887             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31888             e.stopEvent();
31889         }
31890         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
31891         {
31892           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31893           this.field.dom.value = pageNum;
31894           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31895           e.stopEvent();
31896         }
31897         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31898         {
31899           var v = this.field.dom.value, pageNum; 
31900           var increment = (e.shiftKey) ? 10 : 1;
31901           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31902             increment *= -1;
31903           }
31904           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31905             this.field.dom.value = d.activePage;
31906             return;
31907           }
31908           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31909           {
31910             this.field.dom.value = parseInt(v, 10) + increment;
31911             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31912             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31913           }
31914           e.stopEvent();
31915         }
31916     },
31917
31918     // private
31919     beforeLoad : function(){
31920         if(this.loading){
31921             this.loading.disable();
31922         }
31923     },
31924
31925     // private
31926     onClick : function(which){
31927         var ds = this.ds;
31928         switch(which){
31929             case "first":
31930                 ds.load({params:{start: 0, limit: this.pageSize}});
31931             break;
31932             case "prev":
31933                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31934             break;
31935             case "next":
31936                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31937             break;
31938             case "last":
31939                 var total = ds.getTotalCount();
31940                 var extra = total % this.pageSize;
31941                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31942                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31943             break;
31944             case "refresh":
31945                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31946             break;
31947         }
31948     },
31949
31950     /**
31951      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31952      * @param {Roo.data.Store} store The data store to unbind
31953      */
31954     unbind : function(ds){
31955         ds.un("beforeload", this.beforeLoad, this);
31956         ds.un("load", this.onLoad, this);
31957         ds.un("loadexception", this.onLoadError, this);
31958         ds.un("remove", this.updateInfo, this);
31959         ds.un("add", this.updateInfo, this);
31960         this.ds = undefined;
31961     },
31962
31963     /**
31964      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31965      * @param {Roo.data.Store} store The data store to bind
31966      */
31967     bind : function(ds){
31968         ds.on("beforeload", this.beforeLoad, this);
31969         ds.on("load", this.onLoad, this);
31970         ds.on("loadexception", this.onLoadError, this);
31971         ds.on("remove", this.updateInfo, this);
31972         ds.on("add", this.updateInfo, this);
31973         this.ds = ds;
31974     }
31975 });/*
31976  * Based on:
31977  * Ext JS Library 1.1.1
31978  * Copyright(c) 2006-2007, Ext JS, LLC.
31979  *
31980  * Originally Released Under LGPL - original licence link has changed is not relivant.
31981  *
31982  * Fork - LGPL
31983  * <script type="text/javascript">
31984  */
31985
31986 /**
31987  * @class Roo.Resizable
31988  * @extends Roo.util.Observable
31989  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31990  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31991  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
31992  * the element will be wrapped for you automatically.</p>
31993  * <p>Here is the list of valid resize handles:</p>
31994  * <pre>
31995 Value   Description
31996 ------  -------------------
31997  'n'     north
31998  's'     south
31999  'e'     east
32000  'w'     west
32001  'nw'    northwest
32002  'sw'    southwest
32003  'se'    southeast
32004  'ne'    northeast
32005  'hd'    horizontal drag
32006  'all'   all
32007 </pre>
32008  * <p>Here's an example showing the creation of a typical Resizable:</p>
32009  * <pre><code>
32010 var resizer = new Roo.Resizable("element-id", {
32011     handles: 'all',
32012     minWidth: 200,
32013     minHeight: 100,
32014     maxWidth: 500,
32015     maxHeight: 400,
32016     pinned: true
32017 });
32018 resizer.on("resize", myHandler);
32019 </code></pre>
32020  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32021  * resizer.east.setDisplayed(false);</p>
32022  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32023  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32024  * resize operation's new size (defaults to [0, 0])
32025  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32026  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32027  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32028  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32029  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32030  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32031  * @cfg {Number} width The width of the element in pixels (defaults to null)
32032  * @cfg {Number} height The height of the element in pixels (defaults to null)
32033  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32034  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32035  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32036  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32037  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
32038  * in favor of the handles config option (defaults to false)
32039  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32040  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32041  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32042  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32043  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32044  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32045  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32046  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32047  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32048  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32049  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32050  * @constructor
32051  * Create a new resizable component
32052  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32053  * @param {Object} config configuration options
32054   */
32055 Roo.Resizable = function(el, config)
32056 {
32057     this.el = Roo.get(el);
32058
32059     if(config && config.wrap){
32060         config.resizeChild = this.el;
32061         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32062         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32063         this.el.setStyle("overflow", "hidden");
32064         this.el.setPositioning(config.resizeChild.getPositioning());
32065         config.resizeChild.clearPositioning();
32066         if(!config.width || !config.height){
32067             var csize = config.resizeChild.getSize();
32068             this.el.setSize(csize.width, csize.height);
32069         }
32070         if(config.pinned && !config.adjustments){
32071             config.adjustments = "auto";
32072         }
32073     }
32074
32075     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32076     this.proxy.unselectable();
32077     this.proxy.enableDisplayMode('block');
32078
32079     Roo.apply(this, config);
32080
32081     if(this.pinned){
32082         this.disableTrackOver = true;
32083         this.el.addClass("x-resizable-pinned");
32084     }
32085     // if the element isn't positioned, make it relative
32086     var position = this.el.getStyle("position");
32087     if(position != "absolute" && position != "fixed"){
32088         this.el.setStyle("position", "relative");
32089     }
32090     if(!this.handles){ // no handles passed, must be legacy style
32091         this.handles = 's,e,se';
32092         if(this.multiDirectional){
32093             this.handles += ',n,w';
32094         }
32095     }
32096     if(this.handles == "all"){
32097         this.handles = "n s e w ne nw se sw";
32098     }
32099     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32100     var ps = Roo.Resizable.positions;
32101     for(var i = 0, len = hs.length; i < len; i++){
32102         if(hs[i] && ps[hs[i]]){
32103             var pos = ps[hs[i]];
32104             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32105         }
32106     }
32107     // legacy
32108     this.corner = this.southeast;
32109     
32110     // updateBox = the box can move..
32111     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32112         this.updateBox = true;
32113     }
32114
32115     this.activeHandle = null;
32116
32117     if(this.resizeChild){
32118         if(typeof this.resizeChild == "boolean"){
32119             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32120         }else{
32121             this.resizeChild = Roo.get(this.resizeChild, true);
32122         }
32123     }
32124     
32125     if(this.adjustments == "auto"){
32126         var rc = this.resizeChild;
32127         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32128         if(rc && (hw || hn)){
32129             rc.position("relative");
32130             rc.setLeft(hw ? hw.el.getWidth() : 0);
32131             rc.setTop(hn ? hn.el.getHeight() : 0);
32132         }
32133         this.adjustments = [
32134             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32135             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32136         ];
32137     }
32138
32139     if(this.draggable){
32140         this.dd = this.dynamic ?
32141             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32142         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32143     }
32144
32145     // public events
32146     this.addEvents({
32147         /**
32148          * @event beforeresize
32149          * Fired before resize is allowed. Set enabled to false to cancel resize.
32150          * @param {Roo.Resizable} this
32151          * @param {Roo.EventObject} e The mousedown event
32152          */
32153         "beforeresize" : true,
32154         /**
32155          * @event resizing
32156          * Fired a resizing.
32157          * @param {Roo.Resizable} this
32158          * @param {Number} x The new x position
32159          * @param {Number} y The new y position
32160          * @param {Number} w The new w width
32161          * @param {Number} h The new h hight
32162          * @param {Roo.EventObject} e The mouseup event
32163          */
32164         "resizing" : true,
32165         /**
32166          * @event resize
32167          * Fired after a resize.
32168          * @param {Roo.Resizable} this
32169          * @param {Number} width The new width
32170          * @param {Number} height The new height
32171          * @param {Roo.EventObject} e The mouseup event
32172          */
32173         "resize" : true
32174     });
32175
32176     if(this.width !== null && this.height !== null){
32177         this.resizeTo(this.width, this.height);
32178     }else{
32179         this.updateChildSize();
32180     }
32181     if(Roo.isIE){
32182         this.el.dom.style.zoom = 1;
32183     }
32184     Roo.Resizable.superclass.constructor.call(this);
32185 };
32186
32187 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32188         resizeChild : false,
32189         adjustments : [0, 0],
32190         minWidth : 5,
32191         minHeight : 5,
32192         maxWidth : 10000,
32193         maxHeight : 10000,
32194         enabled : true,
32195         animate : false,
32196         duration : .35,
32197         dynamic : false,
32198         handles : false,
32199         multiDirectional : false,
32200         disableTrackOver : false,
32201         easing : 'easeOutStrong',
32202         widthIncrement : 0,
32203         heightIncrement : 0,
32204         pinned : false,
32205         width : null,
32206         height : null,
32207         preserveRatio : false,
32208         transparent: false,
32209         minX: 0,
32210         minY: 0,
32211         draggable: false,
32212
32213         /**
32214          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32215          */
32216         constrainTo: undefined,
32217         /**
32218          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32219          */
32220         resizeRegion: undefined,
32221
32222
32223     /**
32224      * Perform a manual resize
32225      * @param {Number} width
32226      * @param {Number} height
32227      */
32228     resizeTo : function(width, height){
32229         this.el.setSize(width, height);
32230         this.updateChildSize();
32231         this.fireEvent("resize", this, width, height, null);
32232     },
32233
32234     // private
32235     startSizing : function(e, handle){
32236         this.fireEvent("beforeresize", this, e);
32237         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32238
32239             if(!this.overlay){
32240                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32241                 this.overlay.unselectable();
32242                 this.overlay.enableDisplayMode("block");
32243                 this.overlay.on("mousemove", this.onMouseMove, this);
32244                 this.overlay.on("mouseup", this.onMouseUp, this);
32245             }
32246             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32247
32248             this.resizing = true;
32249             this.startBox = this.el.getBox();
32250             this.startPoint = e.getXY();
32251             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32252                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32253
32254             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32255             this.overlay.show();
32256
32257             if(this.constrainTo) {
32258                 var ct = Roo.get(this.constrainTo);
32259                 this.resizeRegion = ct.getRegion().adjust(
32260                     ct.getFrameWidth('t'),
32261                     ct.getFrameWidth('l'),
32262                     -ct.getFrameWidth('b'),
32263                     -ct.getFrameWidth('r')
32264                 );
32265             }
32266
32267             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32268             this.proxy.show();
32269             this.proxy.setBox(this.startBox);
32270             if(!this.dynamic){
32271                 this.proxy.setStyle('visibility', 'visible');
32272             }
32273         }
32274     },
32275
32276     // private
32277     onMouseDown : function(handle, e){
32278         if(this.enabled){
32279             e.stopEvent();
32280             this.activeHandle = handle;
32281             this.startSizing(e, handle);
32282         }
32283     },
32284
32285     // private
32286     onMouseUp : function(e){
32287         var size = this.resizeElement();
32288         this.resizing = false;
32289         this.handleOut();
32290         this.overlay.hide();
32291         this.proxy.hide();
32292         this.fireEvent("resize", this, size.width, size.height, e);
32293     },
32294
32295     // private
32296     updateChildSize : function(){
32297         
32298         if(this.resizeChild){
32299             var el = this.el;
32300             var child = this.resizeChild;
32301             var adj = this.adjustments;
32302             if(el.dom.offsetWidth){
32303                 var b = el.getSize(true);
32304                 child.setSize(b.width+adj[0], b.height+adj[1]);
32305             }
32306             // Second call here for IE
32307             // The first call enables instant resizing and
32308             // the second call corrects scroll bars if they
32309             // exist
32310             if(Roo.isIE){
32311                 setTimeout(function(){
32312                     if(el.dom.offsetWidth){
32313                         var b = el.getSize(true);
32314                         child.setSize(b.width+adj[0], b.height+adj[1]);
32315                     }
32316                 }, 10);
32317             }
32318         }
32319     },
32320
32321     // private
32322     snap : function(value, inc, min){
32323         if(!inc || !value) {
32324             return value;
32325         }
32326         var newValue = value;
32327         var m = value % inc;
32328         if(m > 0){
32329             if(m > (inc/2)){
32330                 newValue = value + (inc-m);
32331             }else{
32332                 newValue = value - m;
32333             }
32334         }
32335         return Math.max(min, newValue);
32336     },
32337
32338     // private
32339     resizeElement : function(){
32340         var box = this.proxy.getBox();
32341         if(this.updateBox){
32342             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32343         }else{
32344             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32345         }
32346         this.updateChildSize();
32347         if(!this.dynamic){
32348             this.proxy.hide();
32349         }
32350         return box;
32351     },
32352
32353     // private
32354     constrain : function(v, diff, m, mx){
32355         if(v - diff < m){
32356             diff = v - m;
32357         }else if(v - diff > mx){
32358             diff = mx - v;
32359         }
32360         return diff;
32361     },
32362
32363     // private
32364     onMouseMove : function(e){
32365         
32366         if(this.enabled){
32367             try{// try catch so if something goes wrong the user doesn't get hung
32368
32369             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32370                 return;
32371             }
32372
32373             //var curXY = this.startPoint;
32374             var curSize = this.curSize || this.startBox;
32375             var x = this.startBox.x, y = this.startBox.y;
32376             var ox = x, oy = y;
32377             var w = curSize.width, h = curSize.height;
32378             var ow = w, oh = h;
32379             var mw = this.minWidth, mh = this.minHeight;
32380             var mxw = this.maxWidth, mxh = this.maxHeight;
32381             var wi = this.widthIncrement;
32382             var hi = this.heightIncrement;
32383
32384             var eventXY = e.getXY();
32385             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32386             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32387
32388             var pos = this.activeHandle.position;
32389
32390             switch(pos){
32391                 case "east":
32392                     w += diffX;
32393                     w = Math.min(Math.max(mw, w), mxw);
32394                     break;
32395              
32396                 case "south":
32397                     h += diffY;
32398                     h = Math.min(Math.max(mh, h), mxh);
32399                     break;
32400                 case "southeast":
32401                     w += diffX;
32402                     h += diffY;
32403                     w = Math.min(Math.max(mw, w), mxw);
32404                     h = Math.min(Math.max(mh, h), mxh);
32405                     break;
32406                 case "north":
32407                     diffY = this.constrain(h, diffY, mh, mxh);
32408                     y += diffY;
32409                     h -= diffY;
32410                     break;
32411                 case "hdrag":
32412                     
32413                     if (wi) {
32414                         var adiffX = Math.abs(diffX);
32415                         var sub = (adiffX % wi); // how much 
32416                         if (sub > (wi/2)) { // far enough to snap
32417                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32418                         } else {
32419                             // remove difference.. 
32420                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32421                         }
32422                     }
32423                     x += diffX;
32424                     x = Math.max(this.minX, x);
32425                     break;
32426                 case "west":
32427                     diffX = this.constrain(w, diffX, mw, mxw);
32428                     x += diffX;
32429                     w -= diffX;
32430                     break;
32431                 case "northeast":
32432                     w += diffX;
32433                     w = Math.min(Math.max(mw, w), mxw);
32434                     diffY = this.constrain(h, diffY, mh, mxh);
32435                     y += diffY;
32436                     h -= diffY;
32437                     break;
32438                 case "northwest":
32439                     diffX = this.constrain(w, diffX, mw, mxw);
32440                     diffY = this.constrain(h, diffY, mh, mxh);
32441                     y += diffY;
32442                     h -= diffY;
32443                     x += diffX;
32444                     w -= diffX;
32445                     break;
32446                case "southwest":
32447                     diffX = this.constrain(w, diffX, mw, mxw);
32448                     h += diffY;
32449                     h = Math.min(Math.max(mh, h), mxh);
32450                     x += diffX;
32451                     w -= diffX;
32452                     break;
32453             }
32454
32455             var sw = this.snap(w, wi, mw);
32456             var sh = this.snap(h, hi, mh);
32457             if(sw != w || sh != h){
32458                 switch(pos){
32459                     case "northeast":
32460                         y -= sh - h;
32461                     break;
32462                     case "north":
32463                         y -= sh - h;
32464                         break;
32465                     case "southwest":
32466                         x -= sw - w;
32467                     break;
32468                     case "west":
32469                         x -= sw - w;
32470                         break;
32471                     case "northwest":
32472                         x -= sw - w;
32473                         y -= sh - h;
32474                     break;
32475                 }
32476                 w = sw;
32477                 h = sh;
32478             }
32479
32480             if(this.preserveRatio){
32481                 switch(pos){
32482                     case "southeast":
32483                     case "east":
32484                         h = oh * (w/ow);
32485                         h = Math.min(Math.max(mh, h), mxh);
32486                         w = ow * (h/oh);
32487                        break;
32488                     case "south":
32489                         w = ow * (h/oh);
32490                         w = Math.min(Math.max(mw, w), mxw);
32491                         h = oh * (w/ow);
32492                         break;
32493                     case "northeast":
32494                         w = ow * (h/oh);
32495                         w = Math.min(Math.max(mw, w), mxw);
32496                         h = oh * (w/ow);
32497                     break;
32498                     case "north":
32499                         var tw = w;
32500                         w = ow * (h/oh);
32501                         w = Math.min(Math.max(mw, w), mxw);
32502                         h = oh * (w/ow);
32503                         x += (tw - w) / 2;
32504                         break;
32505                     case "southwest":
32506                         h = oh * (w/ow);
32507                         h = Math.min(Math.max(mh, h), mxh);
32508                         var tw = w;
32509                         w = ow * (h/oh);
32510                         x += tw - w;
32511                         break;
32512                     case "west":
32513                         var th = h;
32514                         h = oh * (w/ow);
32515                         h = Math.min(Math.max(mh, h), mxh);
32516                         y += (th - h) / 2;
32517                         var tw = w;
32518                         w = ow * (h/oh);
32519                         x += tw - w;
32520                        break;
32521                     case "northwest":
32522                         var tw = w;
32523                         var th = h;
32524                         h = oh * (w/ow);
32525                         h = Math.min(Math.max(mh, h), mxh);
32526                         w = ow * (h/oh);
32527                         y += th - h;
32528                         x += tw - w;
32529                        break;
32530
32531                 }
32532             }
32533             if (pos == 'hdrag') {
32534                 w = ow;
32535             }
32536             this.proxy.setBounds(x, y, w, h);
32537             if(this.dynamic){
32538                 this.resizeElement();
32539             }
32540             }catch(e){}
32541         }
32542         this.fireEvent("resizing", this, x, y, w, h, e);
32543     },
32544
32545     // private
32546     handleOver : function(){
32547         if(this.enabled){
32548             this.el.addClass("x-resizable-over");
32549         }
32550     },
32551
32552     // private
32553     handleOut : function(){
32554         if(!this.resizing){
32555             this.el.removeClass("x-resizable-over");
32556         }
32557     },
32558
32559     /**
32560      * Returns the element this component is bound to.
32561      * @return {Roo.Element}
32562      */
32563     getEl : function(){
32564         return this.el;
32565     },
32566
32567     /**
32568      * Returns the resizeChild element (or null).
32569      * @return {Roo.Element}
32570      */
32571     getResizeChild : function(){
32572         return this.resizeChild;
32573     },
32574     groupHandler : function()
32575     {
32576         
32577     },
32578     /**
32579      * Destroys this resizable. If the element was wrapped and
32580      * removeEl is not true then the element remains.
32581      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32582      */
32583     destroy : function(removeEl){
32584         this.proxy.remove();
32585         if(this.overlay){
32586             this.overlay.removeAllListeners();
32587             this.overlay.remove();
32588         }
32589         var ps = Roo.Resizable.positions;
32590         for(var k in ps){
32591             if(typeof ps[k] != "function" && this[ps[k]]){
32592                 var h = this[ps[k]];
32593                 h.el.removeAllListeners();
32594                 h.el.remove();
32595             }
32596         }
32597         if(removeEl){
32598             this.el.update("");
32599             this.el.remove();
32600         }
32601     }
32602 });
32603
32604 // private
32605 // hash to map config positions to true positions
32606 Roo.Resizable.positions = {
32607     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32608     hd: "hdrag"
32609 };
32610
32611 // private
32612 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32613     if(!this.tpl){
32614         // only initialize the template if resizable is used
32615         var tpl = Roo.DomHelper.createTemplate(
32616             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32617         );
32618         tpl.compile();
32619         Roo.Resizable.Handle.prototype.tpl = tpl;
32620     }
32621     this.position = pos;
32622     this.rz = rz;
32623     // show north drag fro topdra
32624     var handlepos = pos == 'hdrag' ? 'north' : pos;
32625     
32626     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32627     if (pos == 'hdrag') {
32628         this.el.setStyle('cursor', 'pointer');
32629     }
32630     this.el.unselectable();
32631     if(transparent){
32632         this.el.setOpacity(0);
32633     }
32634     this.el.on("mousedown", this.onMouseDown, this);
32635     if(!disableTrackOver){
32636         this.el.on("mouseover", this.onMouseOver, this);
32637         this.el.on("mouseout", this.onMouseOut, this);
32638     }
32639 };
32640
32641 // private
32642 Roo.Resizable.Handle.prototype = {
32643     afterResize : function(rz){
32644         Roo.log('after?');
32645         // do nothing
32646     },
32647     // private
32648     onMouseDown : function(e){
32649         this.rz.onMouseDown(this, e);
32650     },
32651     // private
32652     onMouseOver : function(e){
32653         this.rz.handleOver(this, e);
32654     },
32655     // private
32656     onMouseOut : function(e){
32657         this.rz.handleOut(this, e);
32658     }
32659 };/*
32660  * Based on:
32661  * Ext JS Library 1.1.1
32662  * Copyright(c) 2006-2007, Ext JS, LLC.
32663  *
32664  * Originally Released Under LGPL - original licence link has changed is not relivant.
32665  *
32666  * Fork - LGPL
32667  * <script type="text/javascript">
32668  */
32669
32670 /**
32671  * @class Roo.Editor
32672  * @extends Roo.Component
32673  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32674  * @constructor
32675  * Create a new Editor
32676  * @param {Roo.form.Field} field The Field object (or descendant)
32677  * @param {Object} config The config object
32678  */
32679 Roo.Editor = function(field, config){
32680     Roo.Editor.superclass.constructor.call(this, config);
32681     this.field = field;
32682     this.addEvents({
32683         /**
32684              * @event beforestartedit
32685              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
32686              * false from the handler of this event.
32687              * @param {Editor} this
32688              * @param {Roo.Element} boundEl The underlying element bound to this editor
32689              * @param {Mixed} value The field value being set
32690              */
32691         "beforestartedit" : true,
32692         /**
32693              * @event startedit
32694              * Fires when this editor is displayed
32695              * @param {Roo.Element} boundEl The underlying element bound to this editor
32696              * @param {Mixed} value The starting field value
32697              */
32698         "startedit" : true,
32699         /**
32700              * @event beforecomplete
32701              * Fires after a change has been made to the field, but before the change is reflected in the underlying
32702              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
32703              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
32704              * event will not fire since no edit actually occurred.
32705              * @param {Editor} this
32706              * @param {Mixed} value The current field value
32707              * @param {Mixed} startValue The original field value
32708              */
32709         "beforecomplete" : true,
32710         /**
32711              * @event complete
32712              * Fires after editing is complete and any changed value has been written to the underlying field.
32713              * @param {Editor} this
32714              * @param {Mixed} value The current field value
32715              * @param {Mixed} startValue The original field value
32716              */
32717         "complete" : true,
32718         /**
32719          * @event specialkey
32720          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
32721          * {@link Roo.EventObject#getKey} to determine which key was pressed.
32722          * @param {Roo.form.Field} this
32723          * @param {Roo.EventObject} e The event object
32724          */
32725         "specialkey" : true
32726     });
32727 };
32728
32729 Roo.extend(Roo.Editor, Roo.Component, {
32730     /**
32731      * @cfg {Boolean/String} autosize
32732      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
32733      * or "height" to adopt the height only (defaults to false)
32734      */
32735     /**
32736      * @cfg {Boolean} revertInvalid
32737      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
32738      * validation fails (defaults to true)
32739      */
32740     /**
32741      * @cfg {Boolean} ignoreNoChange
32742      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
32743      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
32744      * will never be ignored.
32745      */
32746     /**
32747      * @cfg {Boolean} hideEl
32748      * False to keep the bound element visible while the editor is displayed (defaults to true)
32749      */
32750     /**
32751      * @cfg {Mixed} value
32752      * The data value of the underlying field (defaults to "")
32753      */
32754     value : "",
32755     /**
32756      * @cfg {String} alignment
32757      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
32758      */
32759     alignment: "c-c?",
32760     /**
32761      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
32762      * for bottom-right shadow (defaults to "frame")
32763      */
32764     shadow : "frame",
32765     /**
32766      * @cfg {Boolean} constrain True to constrain the editor to the viewport
32767      */
32768     constrain : false,
32769     /**
32770      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
32771      */
32772     completeOnEnter : false,
32773     /**
32774      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
32775      */
32776     cancelOnEsc : false,
32777     /**
32778      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
32779      */
32780     updateEl : false,
32781
32782     // private
32783     onRender : function(ct, position){
32784         this.el = new Roo.Layer({
32785             shadow: this.shadow,
32786             cls: "x-editor",
32787             parentEl : ct,
32788             shim : this.shim,
32789             shadowOffset:4,
32790             id: this.id,
32791             constrain: this.constrain
32792         });
32793         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
32794         if(this.field.msgTarget != 'title'){
32795             this.field.msgTarget = 'qtip';
32796         }
32797         this.field.render(this.el);
32798         if(Roo.isGecko){
32799             this.field.el.dom.setAttribute('autocomplete', 'off');
32800         }
32801         this.field.on("specialkey", this.onSpecialKey, this);
32802         if(this.swallowKeys){
32803             this.field.el.swallowEvent(['keydown','keypress']);
32804         }
32805         this.field.show();
32806         this.field.on("blur", this.onBlur, this);
32807         if(this.field.grow){
32808             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
32809         }
32810     },
32811
32812     onSpecialKey : function(field, e)
32813     {
32814         //Roo.log('editor onSpecialKey');
32815         if(this.completeOnEnter && e.getKey() == e.ENTER){
32816             e.stopEvent();
32817             this.completeEdit();
32818             return;
32819         }
32820         // do not fire special key otherwise it might hide close the editor...
32821         if(e.getKey() == e.ENTER){    
32822             return;
32823         }
32824         if(this.cancelOnEsc && e.getKey() == e.ESC){
32825             this.cancelEdit();
32826             return;
32827         } 
32828         this.fireEvent('specialkey', field, e);
32829     
32830     },
32831
32832     /**
32833      * Starts the editing process and shows the editor.
32834      * @param {String/HTMLElement/Element} el The element to edit
32835      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
32836       * to the innerHTML of el.
32837      */
32838     startEdit : function(el, value){
32839         if(this.editing){
32840             this.completeEdit();
32841         }
32842         this.boundEl = Roo.get(el);
32843         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
32844         if(!this.rendered){
32845             this.render(this.parentEl || document.body);
32846         }
32847         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
32848             return;
32849         }
32850         this.startValue = v;
32851         this.field.setValue(v);
32852         if(this.autoSize){
32853             var sz = this.boundEl.getSize();
32854             switch(this.autoSize){
32855                 case "width":
32856                 this.setSize(sz.width,  "");
32857                 break;
32858                 case "height":
32859                 this.setSize("",  sz.height);
32860                 break;
32861                 default:
32862                 this.setSize(sz.width,  sz.height);
32863             }
32864         }
32865         this.el.alignTo(this.boundEl, this.alignment);
32866         this.editing = true;
32867         if(Roo.QuickTips){
32868             Roo.QuickTips.disable();
32869         }
32870         this.show();
32871     },
32872
32873     /**
32874      * Sets the height and width of this editor.
32875      * @param {Number} width The new width
32876      * @param {Number} height The new height
32877      */
32878     setSize : function(w, h){
32879         this.field.setSize(w, h);
32880         if(this.el){
32881             this.el.sync();
32882         }
32883     },
32884
32885     /**
32886      * Realigns the editor to the bound field based on the current alignment config value.
32887      */
32888     realign : function(){
32889         this.el.alignTo(this.boundEl, this.alignment);
32890     },
32891
32892     /**
32893      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
32894      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
32895      */
32896     completeEdit : function(remainVisible){
32897         if(!this.editing){
32898             return;
32899         }
32900         var v = this.getValue();
32901         if(this.revertInvalid !== false && !this.field.isValid()){
32902             v = this.startValue;
32903             this.cancelEdit(true);
32904         }
32905         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32906             this.editing = false;
32907             this.hide();
32908             return;
32909         }
32910         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32911             this.editing = false;
32912             if(this.updateEl && this.boundEl){
32913                 this.boundEl.update(v);
32914             }
32915             if(remainVisible !== true){
32916                 this.hide();
32917             }
32918             this.fireEvent("complete", this, v, this.startValue);
32919         }
32920     },
32921
32922     // private
32923     onShow : function(){
32924         this.el.show();
32925         if(this.hideEl !== false){
32926             this.boundEl.hide();
32927         }
32928         this.field.show();
32929         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32930             this.fixIEFocus = true;
32931             this.deferredFocus.defer(50, this);
32932         }else{
32933             this.field.focus();
32934         }
32935         this.fireEvent("startedit", this.boundEl, this.startValue);
32936     },
32937
32938     deferredFocus : function(){
32939         if(this.editing){
32940             this.field.focus();
32941         }
32942     },
32943
32944     /**
32945      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32946      * reverted to the original starting value.
32947      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32948      * cancel (defaults to false)
32949      */
32950     cancelEdit : function(remainVisible){
32951         if(this.editing){
32952             this.setValue(this.startValue);
32953             if(remainVisible !== true){
32954                 this.hide();
32955             }
32956         }
32957     },
32958
32959     // private
32960     onBlur : function(){
32961         if(this.allowBlur !== true && this.editing){
32962             this.completeEdit();
32963         }
32964     },
32965
32966     // private
32967     onHide : function(){
32968         if(this.editing){
32969             this.completeEdit();
32970             return;
32971         }
32972         this.field.blur();
32973         if(this.field.collapse){
32974             this.field.collapse();
32975         }
32976         this.el.hide();
32977         if(this.hideEl !== false){
32978             this.boundEl.show();
32979         }
32980         if(Roo.QuickTips){
32981             Roo.QuickTips.enable();
32982         }
32983     },
32984
32985     /**
32986      * Sets the data value of the editor
32987      * @param {Mixed} value Any valid value supported by the underlying field
32988      */
32989     setValue : function(v){
32990         this.field.setValue(v);
32991     },
32992
32993     /**
32994      * Gets the data value of the editor
32995      * @return {Mixed} The data value
32996      */
32997     getValue : function(){
32998         return this.field.getValue();
32999     }
33000 });/*
33001  * Based on:
33002  * Ext JS Library 1.1.1
33003  * Copyright(c) 2006-2007, Ext JS, LLC.
33004  *
33005  * Originally Released Under LGPL - original licence link has changed is not relivant.
33006  *
33007  * Fork - LGPL
33008  * <script type="text/javascript">
33009  */
33010  
33011 /**
33012  * @class Roo.BasicDialog
33013  * @extends Roo.util.Observable
33014  * @parent none builder
33015  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
33016  * <pre><code>
33017 var dlg = new Roo.BasicDialog("my-dlg", {
33018     height: 200,
33019     width: 300,
33020     minHeight: 100,
33021     minWidth: 150,
33022     modal: true,
33023     proxyDrag: true,
33024     shadow: true
33025 });
33026 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33027 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
33028 dlg.addButton('Cancel', dlg.hide, dlg);
33029 dlg.show();
33030 </code></pre>
33031   <b>A Dialog should always be a direct child of the body element.</b>
33032  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33033  * @cfg {String} title Default text to display in the title bar (defaults to null)
33034  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33035  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33036  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33037  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33038  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33039  * (defaults to null with no animation)
33040  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33041  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33042  * property for valid values (defaults to 'all')
33043  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33044  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33045  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33046  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33047  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33048  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33049  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33050  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33051  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33052  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33053  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33054  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33055  * draggable = true (defaults to false)
33056  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33057  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33058  * shadow (defaults to false)
33059  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33060  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33061  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33062  * @cfg {Array} buttons Array of buttons
33063  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33064  * @constructor
33065  * Create a new BasicDialog.
33066  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33067  * @param {Object} config Configuration options
33068  */
33069 Roo.BasicDialog = function(el, config){
33070     this.el = Roo.get(el);
33071     var dh = Roo.DomHelper;
33072     if(!this.el && config && config.autoCreate){
33073         if(typeof config.autoCreate == "object"){
33074             if(!config.autoCreate.id){
33075                 config.autoCreate.id = el;
33076             }
33077             this.el = dh.append(document.body,
33078                         config.autoCreate, true);
33079         }else{
33080             this.el = dh.append(document.body,
33081                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33082         }
33083     }
33084     el = this.el;
33085     el.setDisplayed(true);
33086     el.hide = this.hideAction;
33087     this.id = el.id;
33088     el.addClass("x-dlg");
33089
33090     Roo.apply(this, config);
33091
33092     this.proxy = el.createProxy("x-dlg-proxy");
33093     this.proxy.hide = this.hideAction;
33094     this.proxy.setOpacity(.5);
33095     this.proxy.hide();
33096
33097     if(config.width){
33098         el.setWidth(config.width);
33099     }
33100     if(config.height){
33101         el.setHeight(config.height);
33102     }
33103     this.size = el.getSize();
33104     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33105         this.xy = [config.x,config.y];
33106     }else{
33107         this.xy = el.getCenterXY(true);
33108     }
33109     /** The header element @type Roo.Element */
33110     this.header = el.child("> .x-dlg-hd");
33111     /** The body element @type Roo.Element */
33112     this.body = el.child("> .x-dlg-bd");
33113     /** The footer element @type Roo.Element */
33114     this.footer = el.child("> .x-dlg-ft");
33115
33116     if(!this.header){
33117         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33118     }
33119     if(!this.body){
33120         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33121     }
33122
33123     this.header.unselectable();
33124     if(this.title){
33125         this.header.update(this.title);
33126     }
33127     // this element allows the dialog to be focused for keyboard event
33128     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33129     this.focusEl.swallowEvent("click", true);
33130
33131     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33132
33133     // wrap the body and footer for special rendering
33134     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33135     if(this.footer){
33136         this.bwrap.dom.appendChild(this.footer.dom);
33137     }
33138
33139     this.bg = this.el.createChild({
33140         tag: "div", cls:"x-dlg-bg",
33141         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33142     });
33143     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33144
33145
33146     if(this.autoScroll !== false && !this.autoTabs){
33147         this.body.setStyle("overflow", "auto");
33148     }
33149
33150     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33151
33152     if(this.closable !== false){
33153         this.el.addClass("x-dlg-closable");
33154         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33155         this.close.on("click", this.closeClick, this);
33156         this.close.addClassOnOver("x-dlg-close-over");
33157     }
33158     if(this.collapsible !== false){
33159         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33160         this.collapseBtn.on("click", this.collapseClick, this);
33161         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33162         this.header.on("dblclick", this.collapseClick, this);
33163     }
33164     if(this.resizable !== false){
33165         this.el.addClass("x-dlg-resizable");
33166         this.resizer = new Roo.Resizable(el, {
33167             minWidth: this.minWidth || 80,
33168             minHeight:this.minHeight || 80,
33169             handles: this.resizeHandles || "all",
33170             pinned: true
33171         });
33172         this.resizer.on("beforeresize", this.beforeResize, this);
33173         this.resizer.on("resize", this.onResize, this);
33174     }
33175     if(this.draggable !== false){
33176         el.addClass("x-dlg-draggable");
33177         if (!this.proxyDrag) {
33178             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33179         }
33180         else {
33181             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33182         }
33183         dd.setHandleElId(this.header.id);
33184         dd.endDrag = this.endMove.createDelegate(this);
33185         dd.startDrag = this.startMove.createDelegate(this);
33186         dd.onDrag = this.onDrag.createDelegate(this);
33187         dd.scroll = false;
33188         this.dd = dd;
33189     }
33190     if(this.modal){
33191         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33192         this.mask.enableDisplayMode("block");
33193         this.mask.hide();
33194         this.el.addClass("x-dlg-modal");
33195     }
33196     if(this.shadow){
33197         this.shadow = new Roo.Shadow({
33198             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33199             offset : this.shadowOffset
33200         });
33201     }else{
33202         this.shadowOffset = 0;
33203     }
33204     if(Roo.useShims && this.shim !== false){
33205         this.shim = this.el.createShim();
33206         this.shim.hide = this.hideAction;
33207         this.shim.hide();
33208     }else{
33209         this.shim = false;
33210     }
33211     if(this.autoTabs){
33212         this.initTabs();
33213     }
33214     if (this.buttons) { 
33215         var bts= this.buttons;
33216         this.buttons = [];
33217         Roo.each(bts, function(b) {
33218             this.addButton(b);
33219         }, this);
33220     }
33221     
33222     
33223     this.addEvents({
33224         /**
33225          * @event keydown
33226          * Fires when a key is pressed
33227          * @param {Roo.BasicDialog} this
33228          * @param {Roo.EventObject} e
33229          */
33230         "keydown" : true,
33231         /**
33232          * @event move
33233          * Fires when this dialog is moved by the user.
33234          * @param {Roo.BasicDialog} this
33235          * @param {Number} x The new page X
33236          * @param {Number} y The new page Y
33237          */
33238         "move" : true,
33239         /**
33240          * @event resize
33241          * Fires when this dialog is resized by the user.
33242          * @param {Roo.BasicDialog} this
33243          * @param {Number} width The new width
33244          * @param {Number} height The new height
33245          */
33246         "resize" : true,
33247         /**
33248          * @event beforehide
33249          * Fires before this dialog is hidden.
33250          * @param {Roo.BasicDialog} this
33251          */
33252         "beforehide" : true,
33253         /**
33254          * @event hide
33255          * Fires when this dialog is hidden.
33256          * @param {Roo.BasicDialog} this
33257          */
33258         "hide" : true,
33259         /**
33260          * @event beforeshow
33261          * Fires before this dialog is shown.
33262          * @param {Roo.BasicDialog} this
33263          */
33264         "beforeshow" : true,
33265         /**
33266          * @event show
33267          * Fires when this dialog is shown.
33268          * @param {Roo.BasicDialog} this
33269          */
33270         "show" : true
33271     });
33272     el.on("keydown", this.onKeyDown, this);
33273     el.on("mousedown", this.toFront, this);
33274     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33275     this.el.hide();
33276     Roo.DialogManager.register(this);
33277     Roo.BasicDialog.superclass.constructor.call(this);
33278 };
33279
33280 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33281     shadowOffset: Roo.isIE ? 6 : 5,
33282     minHeight: 80,
33283     minWidth: 200,
33284     minButtonWidth: 75,
33285     defaultButton: null,
33286     buttonAlign: "right",
33287     tabTag: 'div',
33288     firstShow: true,
33289
33290     /**
33291      * Sets the dialog title text
33292      * @param {String} text The title text to display
33293      * @return {Roo.BasicDialog} this
33294      */
33295     setTitle : function(text){
33296         this.header.update(text);
33297         return this;
33298     },
33299
33300     // private
33301     closeClick : function(){
33302         this.hide();
33303     },
33304
33305     // private
33306     collapseClick : function(){
33307         this[this.collapsed ? "expand" : "collapse"]();
33308     },
33309
33310     /**
33311      * Collapses the dialog to its minimized state (only the title bar is visible).
33312      * Equivalent to the user clicking the collapse dialog button.
33313      */
33314     collapse : function(){
33315         if(!this.collapsed){
33316             this.collapsed = true;
33317             this.el.addClass("x-dlg-collapsed");
33318             this.restoreHeight = this.el.getHeight();
33319             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33320         }
33321     },
33322
33323     /**
33324      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33325      * clicking the expand dialog button.
33326      */
33327     expand : function(){
33328         if(this.collapsed){
33329             this.collapsed = false;
33330             this.el.removeClass("x-dlg-collapsed");
33331             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33332         }
33333     },
33334
33335     /**
33336      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33337      * @return {Roo.TabPanel} The tabs component
33338      */
33339     initTabs : function(){
33340         var tabs = this.getTabs();
33341         while(tabs.getTab(0)){
33342             tabs.removeTab(0);
33343         }
33344         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33345             var dom = el.dom;
33346             tabs.addTab(Roo.id(dom), dom.title);
33347             dom.title = "";
33348         });
33349         tabs.activate(0);
33350         return tabs;
33351     },
33352
33353     // private
33354     beforeResize : function(){
33355         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33356     },
33357
33358     // private
33359     onResize : function(){
33360         this.refreshSize();
33361         this.syncBodyHeight();
33362         this.adjustAssets();
33363         this.focus();
33364         this.fireEvent("resize", this, this.size.width, this.size.height);
33365     },
33366
33367     // private
33368     onKeyDown : function(e){
33369         if(this.isVisible()){
33370             this.fireEvent("keydown", this, e);
33371         }
33372     },
33373
33374     /**
33375      * Resizes the dialog.
33376      * @param {Number} width
33377      * @param {Number} height
33378      * @return {Roo.BasicDialog} this
33379      */
33380     resizeTo : function(width, height){
33381         this.el.setSize(width, height);
33382         this.size = {width: width, height: height};
33383         this.syncBodyHeight();
33384         if(this.fixedcenter){
33385             this.center();
33386         }
33387         if(this.isVisible()){
33388             this.constrainXY();
33389             this.adjustAssets();
33390         }
33391         this.fireEvent("resize", this, width, height);
33392         return this;
33393     },
33394
33395
33396     /**
33397      * Resizes the dialog to fit the specified content size.
33398      * @param {Number} width
33399      * @param {Number} height
33400      * @return {Roo.BasicDialog} this
33401      */
33402     setContentSize : function(w, h){
33403         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33404         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33405         //if(!this.el.isBorderBox()){
33406             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33407             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33408         //}
33409         if(this.tabs){
33410             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33411             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33412         }
33413         this.resizeTo(w, h);
33414         return this;
33415     },
33416
33417     /**
33418      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33419      * executed in response to a particular key being pressed while the dialog is active.
33420      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33421      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33422      * @param {Function} fn The function to call
33423      * @param {Object} scope (optional) The scope of the function
33424      * @return {Roo.BasicDialog} this
33425      */
33426     addKeyListener : function(key, fn, scope){
33427         var keyCode, shift, ctrl, alt;
33428         if(typeof key == "object" && !(key instanceof Array)){
33429             keyCode = key["key"];
33430             shift = key["shift"];
33431             ctrl = key["ctrl"];
33432             alt = key["alt"];
33433         }else{
33434             keyCode = key;
33435         }
33436         var handler = function(dlg, e){
33437             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33438                 var k = e.getKey();
33439                 if(keyCode instanceof Array){
33440                     for(var i = 0, len = keyCode.length; i < len; i++){
33441                         if(keyCode[i] == k){
33442                           fn.call(scope || window, dlg, k, e);
33443                           return;
33444                         }
33445                     }
33446                 }else{
33447                     if(k == keyCode){
33448                         fn.call(scope || window, dlg, k, e);
33449                     }
33450                 }
33451             }
33452         };
33453         this.on("keydown", handler);
33454         return this;
33455     },
33456
33457     /**
33458      * Returns the TabPanel component (creates it if it doesn't exist).
33459      * Note: If you wish to simply check for the existence of tabs without creating them,
33460      * check for a null 'tabs' property.
33461      * @return {Roo.TabPanel} The tabs component
33462      */
33463     getTabs : function(){
33464         if(!this.tabs){
33465             this.el.addClass("x-dlg-auto-tabs");
33466             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33467             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33468         }
33469         return this.tabs;
33470     },
33471
33472     /**
33473      * Adds a button to the footer section of the dialog.
33474      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33475      * object or a valid Roo.DomHelper element config
33476      * @param {Function} handler The function called when the button is clicked
33477      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33478      * @return {Roo.Button} The new button
33479      */
33480     addButton : function(config, handler, scope){
33481         var dh = Roo.DomHelper;
33482         if(!this.footer){
33483             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33484         }
33485         if(!this.btnContainer){
33486             var tb = this.footer.createChild({
33487
33488                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33489                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33490             }, null, true);
33491             this.btnContainer = tb.firstChild.firstChild.firstChild;
33492         }
33493         var bconfig = {
33494             handler: handler,
33495             scope: scope,
33496             minWidth: this.minButtonWidth,
33497             hideParent:true
33498         };
33499         if(typeof config == "string"){
33500             bconfig.text = config;
33501         }else{
33502             if(config.tag){
33503                 bconfig.dhconfig = config;
33504             }else{
33505                 Roo.apply(bconfig, config);
33506             }
33507         }
33508         var fc = false;
33509         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33510             bconfig.position = Math.max(0, bconfig.position);
33511             fc = this.btnContainer.childNodes[bconfig.position];
33512         }
33513          
33514         var btn = new Roo.Button(
33515             fc ? 
33516                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33517                 : this.btnContainer.appendChild(document.createElement("td")),
33518             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33519             bconfig
33520         );
33521         this.syncBodyHeight();
33522         if(!this.buttons){
33523             /**
33524              * Array of all the buttons that have been added to this dialog via addButton
33525              * @type Array
33526              */
33527             this.buttons = [];
33528         }
33529         this.buttons.push(btn);
33530         return btn;
33531     },
33532
33533     /**
33534      * Sets the default button to be focused when the dialog is displayed.
33535      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33536      * @return {Roo.BasicDialog} this
33537      */
33538     setDefaultButton : function(btn){
33539         this.defaultButton = btn;
33540         return this;
33541     },
33542
33543     // private
33544     getHeaderFooterHeight : function(safe){
33545         var height = 0;
33546         if(this.header){
33547            height += this.header.getHeight();
33548         }
33549         if(this.footer){
33550            var fm = this.footer.getMargins();
33551             height += (this.footer.getHeight()+fm.top+fm.bottom);
33552         }
33553         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33554         height += this.centerBg.getPadding("tb");
33555         return height;
33556     },
33557
33558     // private
33559     syncBodyHeight : function()
33560     {
33561         var bd = this.body, // the text
33562             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33563             bw = this.bwrap;
33564         var height = this.size.height - this.getHeaderFooterHeight(false);
33565         bd.setHeight(height-bd.getMargins("tb"));
33566         var hh = this.header.getHeight();
33567         var h = this.size.height-hh;
33568         cb.setHeight(h);
33569         
33570         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33571         bw.setHeight(h-cb.getPadding("tb"));
33572         
33573         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33574         bd.setWidth(bw.getWidth(true));
33575         if(this.tabs){
33576             this.tabs.syncHeight();
33577             if(Roo.isIE){
33578                 this.tabs.el.repaint();
33579             }
33580         }
33581     },
33582
33583     /**
33584      * Restores the previous state of the dialog if Roo.state is configured.
33585      * @return {Roo.BasicDialog} this
33586      */
33587     restoreState : function(){
33588         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33589         if(box && box.width){
33590             this.xy = [box.x, box.y];
33591             this.resizeTo(box.width, box.height);
33592         }
33593         return this;
33594     },
33595
33596     // private
33597     beforeShow : function(){
33598         this.expand();
33599         if(this.fixedcenter){
33600             this.xy = this.el.getCenterXY(true);
33601         }
33602         if(this.modal){
33603             Roo.get(document.body).addClass("x-body-masked");
33604             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33605             this.mask.show();
33606         }
33607         this.constrainXY();
33608     },
33609
33610     // private
33611     animShow : function(){
33612         var b = Roo.get(this.animateTarget).getBox();
33613         this.proxy.setSize(b.width, b.height);
33614         this.proxy.setLocation(b.x, b.y);
33615         this.proxy.show();
33616         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33617                     true, .35, this.showEl.createDelegate(this));
33618     },
33619
33620     /**
33621      * Shows the dialog.
33622      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33623      * @return {Roo.BasicDialog} this
33624      */
33625     show : function(animateTarget){
33626         if (this.fireEvent("beforeshow", this) === false){
33627             return;
33628         }
33629         if(this.syncHeightBeforeShow){
33630             this.syncBodyHeight();
33631         }else if(this.firstShow){
33632             this.firstShow = false;
33633             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33634         }
33635         this.animateTarget = animateTarget || this.animateTarget;
33636         if(!this.el.isVisible()){
33637             this.beforeShow();
33638             if(this.animateTarget && Roo.get(this.animateTarget)){
33639                 this.animShow();
33640             }else{
33641                 this.showEl();
33642             }
33643         }
33644         return this;
33645     },
33646
33647     // private
33648     showEl : function(){
33649         this.proxy.hide();
33650         this.el.setXY(this.xy);
33651         this.el.show();
33652         this.adjustAssets(true);
33653         this.toFront();
33654         this.focus();
33655         // IE peekaboo bug - fix found by Dave Fenwick
33656         if(Roo.isIE){
33657             this.el.repaint();
33658         }
33659         this.fireEvent("show", this);
33660     },
33661
33662     /**
33663      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33664      * dialog itself will receive focus.
33665      */
33666     focus : function(){
33667         if(this.defaultButton){
33668             this.defaultButton.focus();
33669         }else{
33670             this.focusEl.focus();
33671         }
33672     },
33673
33674     // private
33675     constrainXY : function(){
33676         if(this.constraintoviewport !== false){
33677             if(!this.viewSize){
33678                 if(this.container){
33679                     var s = this.container.getSize();
33680                     this.viewSize = [s.width, s.height];
33681                 }else{
33682                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33683                 }
33684             }
33685             var s = Roo.get(this.container||document).getScroll();
33686
33687             var x = this.xy[0], y = this.xy[1];
33688             var w = this.size.width, h = this.size.height;
33689             var vw = this.viewSize[0], vh = this.viewSize[1];
33690             // only move it if it needs it
33691             var moved = false;
33692             // first validate right/bottom
33693             if(x + w > vw+s.left){
33694                 x = vw - w;
33695                 moved = true;
33696             }
33697             if(y + h > vh+s.top){
33698                 y = vh - h;
33699                 moved = true;
33700             }
33701             // then make sure top/left isn't negative
33702             if(x < s.left){
33703                 x = s.left;
33704                 moved = true;
33705             }
33706             if(y < s.top){
33707                 y = s.top;
33708                 moved = true;
33709             }
33710             if(moved){
33711                 // cache xy
33712                 this.xy = [x, y];
33713                 if(this.isVisible()){
33714                     this.el.setLocation(x, y);
33715                     this.adjustAssets();
33716                 }
33717             }
33718         }
33719     },
33720
33721     // private
33722     onDrag : function(){
33723         if(!this.proxyDrag){
33724             this.xy = this.el.getXY();
33725             this.adjustAssets();
33726         }
33727     },
33728
33729     // private
33730     adjustAssets : function(doShow){
33731         var x = this.xy[0], y = this.xy[1];
33732         var w = this.size.width, h = this.size.height;
33733         if(doShow === true){
33734             if(this.shadow){
33735                 this.shadow.show(this.el);
33736             }
33737             if(this.shim){
33738                 this.shim.show();
33739             }
33740         }
33741         if(this.shadow && this.shadow.isVisible()){
33742             this.shadow.show(this.el);
33743         }
33744         if(this.shim && this.shim.isVisible()){
33745             this.shim.setBounds(x, y, w, h);
33746         }
33747     },
33748
33749     // private
33750     adjustViewport : function(w, h){
33751         if(!w || !h){
33752             w = Roo.lib.Dom.getViewWidth();
33753             h = Roo.lib.Dom.getViewHeight();
33754         }
33755         // cache the size
33756         this.viewSize = [w, h];
33757         if(this.modal && this.mask.isVisible()){
33758             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
33759             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33760         }
33761         if(this.isVisible()){
33762             this.constrainXY();
33763         }
33764     },
33765
33766     /**
33767      * Destroys this dialog and all its supporting elements (including any tabs, shim,
33768      * shadow, proxy, mask, etc.)  Also removes all event listeners.
33769      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33770      */
33771     destroy : function(removeEl){
33772         if(this.isVisible()){
33773             this.animateTarget = null;
33774             this.hide();
33775         }
33776         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
33777         if(this.tabs){
33778             this.tabs.destroy(removeEl);
33779         }
33780         Roo.destroy(
33781              this.shim,
33782              this.proxy,
33783              this.resizer,
33784              this.close,
33785              this.mask
33786         );
33787         if(this.dd){
33788             this.dd.unreg();
33789         }
33790         if(this.buttons){
33791            for(var i = 0, len = this.buttons.length; i < len; i++){
33792                this.buttons[i].destroy();
33793            }
33794         }
33795         this.el.removeAllListeners();
33796         if(removeEl === true){
33797             this.el.update("");
33798             this.el.remove();
33799         }
33800         Roo.DialogManager.unregister(this);
33801     },
33802
33803     // private
33804     startMove : function(){
33805         if(this.proxyDrag){
33806             this.proxy.show();
33807         }
33808         if(this.constraintoviewport !== false){
33809             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
33810         }
33811     },
33812
33813     // private
33814     endMove : function(){
33815         if(!this.proxyDrag){
33816             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
33817         }else{
33818             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
33819             this.proxy.hide();
33820         }
33821         this.refreshSize();
33822         this.adjustAssets();
33823         this.focus();
33824         this.fireEvent("move", this, this.xy[0], this.xy[1]);
33825     },
33826
33827     /**
33828      * Brings this dialog to the front of any other visible dialogs
33829      * @return {Roo.BasicDialog} this
33830      */
33831     toFront : function(){
33832         Roo.DialogManager.bringToFront(this);
33833         return this;
33834     },
33835
33836     /**
33837      * Sends this dialog to the back (under) of any other visible dialogs
33838      * @return {Roo.BasicDialog} this
33839      */
33840     toBack : function(){
33841         Roo.DialogManager.sendToBack(this);
33842         return this;
33843     },
33844
33845     /**
33846      * Centers this dialog in the viewport
33847      * @return {Roo.BasicDialog} this
33848      */
33849     center : function(){
33850         var xy = this.el.getCenterXY(true);
33851         this.moveTo(xy[0], xy[1]);
33852         return this;
33853     },
33854
33855     /**
33856      * Moves the dialog's top-left corner to the specified point
33857      * @param {Number} x
33858      * @param {Number} y
33859      * @return {Roo.BasicDialog} this
33860      */
33861     moveTo : function(x, y){
33862         this.xy = [x,y];
33863         if(this.isVisible()){
33864             this.el.setXY(this.xy);
33865             this.adjustAssets();
33866         }
33867         return this;
33868     },
33869
33870     /**
33871      * Aligns the dialog to the specified element
33872      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33873      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
33874      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33875      * @return {Roo.BasicDialog} this
33876      */
33877     alignTo : function(element, position, offsets){
33878         this.xy = this.el.getAlignToXY(element, position, offsets);
33879         if(this.isVisible()){
33880             this.el.setXY(this.xy);
33881             this.adjustAssets();
33882         }
33883         return this;
33884     },
33885
33886     /**
33887      * Anchors an element to another element and realigns it when the window is resized.
33888      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33889      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
33890      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33891      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
33892      * is a number, it is used as the buffer delay (defaults to 50ms).
33893      * @return {Roo.BasicDialog} this
33894      */
33895     anchorTo : function(el, alignment, offsets, monitorScroll){
33896         var action = function(){
33897             this.alignTo(el, alignment, offsets);
33898         };
33899         Roo.EventManager.onWindowResize(action, this);
33900         var tm = typeof monitorScroll;
33901         if(tm != 'undefined'){
33902             Roo.EventManager.on(window, 'scroll', action, this,
33903                 {buffer: tm == 'number' ? monitorScroll : 50});
33904         }
33905         action.call(this);
33906         return this;
33907     },
33908
33909     /**
33910      * Returns true if the dialog is visible
33911      * @return {Boolean}
33912      */
33913     isVisible : function(){
33914         return this.el.isVisible();
33915     },
33916
33917     // private
33918     animHide : function(callback){
33919         var b = Roo.get(this.animateTarget).getBox();
33920         this.proxy.show();
33921         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33922         this.el.hide();
33923         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33924                     this.hideEl.createDelegate(this, [callback]));
33925     },
33926
33927     /**
33928      * Hides the dialog.
33929      * @param {Function} callback (optional) Function to call when the dialog is hidden
33930      * @return {Roo.BasicDialog} this
33931      */
33932     hide : function(callback){
33933         if (this.fireEvent("beforehide", this) === false){
33934             return;
33935         }
33936         if(this.shadow){
33937             this.shadow.hide();
33938         }
33939         if(this.shim) {
33940           this.shim.hide();
33941         }
33942         // sometimes animateTarget seems to get set.. causing problems...
33943         // this just double checks..
33944         if(this.animateTarget && Roo.get(this.animateTarget)) {
33945            this.animHide(callback);
33946         }else{
33947             this.el.hide();
33948             this.hideEl(callback);
33949         }
33950         return this;
33951     },
33952
33953     // private
33954     hideEl : function(callback){
33955         this.proxy.hide();
33956         if(this.modal){
33957             this.mask.hide();
33958             Roo.get(document.body).removeClass("x-body-masked");
33959         }
33960         this.fireEvent("hide", this);
33961         if(typeof callback == "function"){
33962             callback();
33963         }
33964     },
33965
33966     // private
33967     hideAction : function(){
33968         this.setLeft("-10000px");
33969         this.setTop("-10000px");
33970         this.setStyle("visibility", "hidden");
33971     },
33972
33973     // private
33974     refreshSize : function(){
33975         this.size = this.el.getSize();
33976         this.xy = this.el.getXY();
33977         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33978     },
33979
33980     // private
33981     // z-index is managed by the DialogManager and may be overwritten at any time
33982     setZIndex : function(index){
33983         if(this.modal){
33984             this.mask.setStyle("z-index", index);
33985         }
33986         if(this.shim){
33987             this.shim.setStyle("z-index", ++index);
33988         }
33989         if(this.shadow){
33990             this.shadow.setZIndex(++index);
33991         }
33992         this.el.setStyle("z-index", ++index);
33993         if(this.proxy){
33994             this.proxy.setStyle("z-index", ++index);
33995         }
33996         if(this.resizer){
33997             this.resizer.proxy.setStyle("z-index", ++index);
33998         }
33999
34000         this.lastZIndex = index;
34001     },
34002
34003     /**
34004      * Returns the element for this dialog
34005      * @return {Roo.Element} The underlying dialog Element
34006      */
34007     getEl : function(){
34008         return this.el;
34009     }
34010 });
34011
34012 /**
34013  * @class Roo.DialogManager
34014  * Provides global access to BasicDialogs that have been created and
34015  * support for z-indexing (layering) multiple open dialogs.
34016  */
34017 Roo.DialogManager = function(){
34018     var list = {};
34019     var accessList = [];
34020     var front = null;
34021
34022     // private
34023     var sortDialogs = function(d1, d2){
34024         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34025     };
34026
34027     // private
34028     var orderDialogs = function(){
34029         accessList.sort(sortDialogs);
34030         var seed = Roo.DialogManager.zseed;
34031         for(var i = 0, len = accessList.length; i < len; i++){
34032             var dlg = accessList[i];
34033             if(dlg){
34034                 dlg.setZIndex(seed + (i*10));
34035             }
34036         }
34037     };
34038
34039     return {
34040         /**
34041          * The starting z-index for BasicDialogs (defaults to 9000)
34042          * @type Number The z-index value
34043          */
34044         zseed : 9000,
34045
34046         // private
34047         register : function(dlg){
34048             list[dlg.id] = dlg;
34049             accessList.push(dlg);
34050         },
34051
34052         // private
34053         unregister : function(dlg){
34054             delete list[dlg.id];
34055             var i=0;
34056             var len=0;
34057             if(!accessList.indexOf){
34058                 for(  i = 0, len = accessList.length; i < len; i++){
34059                     if(accessList[i] == dlg){
34060                         accessList.splice(i, 1);
34061                         return;
34062                     }
34063                 }
34064             }else{
34065                  i = accessList.indexOf(dlg);
34066                 if(i != -1){
34067                     accessList.splice(i, 1);
34068                 }
34069             }
34070         },
34071
34072         /**
34073          * Gets a registered dialog by id
34074          * @param {String/Object} id The id of the dialog or a dialog
34075          * @return {Roo.BasicDialog} this
34076          */
34077         get : function(id){
34078             return typeof id == "object" ? id : list[id];
34079         },
34080
34081         /**
34082          * Brings the specified dialog to the front
34083          * @param {String/Object} dlg The id of the dialog or a dialog
34084          * @return {Roo.BasicDialog} this
34085          */
34086         bringToFront : function(dlg){
34087             dlg = this.get(dlg);
34088             if(dlg != front){
34089                 front = dlg;
34090                 dlg._lastAccess = new Date().getTime();
34091                 orderDialogs();
34092             }
34093             return dlg;
34094         },
34095
34096         /**
34097          * Sends the specified dialog to the back
34098          * @param {String/Object} dlg The id of the dialog or a dialog
34099          * @return {Roo.BasicDialog} this
34100          */
34101         sendToBack : function(dlg){
34102             dlg = this.get(dlg);
34103             dlg._lastAccess = -(new Date().getTime());
34104             orderDialogs();
34105             return dlg;
34106         },
34107
34108         /**
34109          * Hides all dialogs
34110          */
34111         hideAll : function(){
34112             for(var id in list){
34113                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34114                     list[id].hide();
34115                 }
34116             }
34117         }
34118     };
34119 }();
34120
34121 /**
34122  * @class Roo.LayoutDialog
34123  * @extends Roo.BasicDialog
34124  * @children Roo.ContentPanel
34125  * @parent builder none
34126  * Dialog which provides adjustments for working with a layout in a Dialog.
34127  * Add your necessary layout config options to the dialog's config.<br>
34128  * Example usage (including a nested layout):
34129  * <pre><code>
34130 if(!dialog){
34131     dialog = new Roo.LayoutDialog("download-dlg", {
34132         modal: true,
34133         width:600,
34134         height:450,
34135         shadow:true,
34136         minWidth:500,
34137         minHeight:350,
34138         autoTabs:true,
34139         proxyDrag:true,
34140         // layout config merges with the dialog config
34141         center:{
34142             tabPosition: "top",
34143             alwaysShowTabs: true
34144         }
34145     });
34146     dialog.addKeyListener(27, dialog.hide, dialog);
34147     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34148     dialog.addButton("Build It!", this.getDownload, this);
34149
34150     // we can even add nested layouts
34151     var innerLayout = new Roo.BorderLayout("dl-inner", {
34152         east: {
34153             initialSize: 200,
34154             autoScroll:true,
34155             split:true
34156         },
34157         center: {
34158             autoScroll:true
34159         }
34160     });
34161     innerLayout.beginUpdate();
34162     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34163     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34164     innerLayout.endUpdate(true);
34165
34166     var layout = dialog.getLayout();
34167     layout.beginUpdate();
34168     layout.add("center", new Roo.ContentPanel("standard-panel",
34169                         {title: "Download the Source", fitToFrame:true}));
34170     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34171                {title: "Build your own roo.js"}));
34172     layout.getRegion("center").showPanel(sp);
34173     layout.endUpdate();
34174 }
34175 </code></pre>
34176     * @constructor
34177     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34178     * @param {Object} config configuration options
34179   */
34180 Roo.LayoutDialog = function(el, cfg){
34181     
34182     var config=  cfg;
34183     if (typeof(cfg) == 'undefined') {
34184         config = Roo.apply({}, el);
34185         // not sure why we use documentElement here.. - it should always be body.
34186         // IE7 borks horribly if we use documentElement.
34187         // webkit also does not like documentElement - it creates a body element...
34188         el = Roo.get( document.body || document.documentElement ).createChild();
34189         //config.autoCreate = true;
34190     }
34191     
34192     
34193     config.autoTabs = false;
34194     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34195     this.body.setStyle({overflow:"hidden", position:"relative"});
34196     this.layout = new Roo.BorderLayout(this.body.dom, config);
34197     this.layout.monitorWindowResize = false;
34198     this.el.addClass("x-dlg-auto-layout");
34199     // fix case when center region overwrites center function
34200     this.center = Roo.BasicDialog.prototype.center;
34201     this.on("show", this.layout.layout, this.layout, true);
34202     if (config.items) {
34203         var xitems = config.items;
34204         delete config.items;
34205         Roo.each(xitems, this.addxtype, this);
34206     }
34207     
34208     
34209 };
34210 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34211     
34212     
34213     /**
34214      * @cfg {Roo.LayoutRegion} east  
34215      */
34216     /**
34217      * @cfg {Roo.LayoutRegion} west
34218      */
34219     /**
34220      * @cfg {Roo.LayoutRegion} south
34221      */
34222     /**
34223      * @cfg {Roo.LayoutRegion} north
34224      */
34225     /**
34226      * @cfg {Roo.LayoutRegion} center
34227      */
34228     /**
34229      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34230      */
34231     
34232     
34233     /**
34234      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34235      * @deprecated
34236      */
34237     endUpdate : function(){
34238         this.layout.endUpdate();
34239     },
34240
34241     /**
34242      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34243      *  @deprecated
34244      */
34245     beginUpdate : function(){
34246         this.layout.beginUpdate();
34247     },
34248
34249     /**
34250      * Get the BorderLayout for this dialog
34251      * @return {Roo.BorderLayout}
34252      */
34253     getLayout : function(){
34254         return this.layout;
34255     },
34256
34257     showEl : function(){
34258         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34259         if(Roo.isIE7){
34260             this.layout.layout();
34261         }
34262     },
34263
34264     // private
34265     // Use the syncHeightBeforeShow config option to control this automatically
34266     syncBodyHeight : function(){
34267         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34268         if(this.layout){this.layout.layout();}
34269     },
34270     
34271       /**
34272      * Add an xtype element (actually adds to the layout.)
34273      * @return {Object} xdata xtype object data.
34274      */
34275     
34276     addxtype : function(c) {
34277         return this.layout.addxtype(c);
34278     }
34279 });/*
34280  * Based on:
34281  * Ext JS Library 1.1.1
34282  * Copyright(c) 2006-2007, Ext JS, LLC.
34283  *
34284  * Originally Released Under LGPL - original licence link has changed is not relivant.
34285  *
34286  * Fork - LGPL
34287  * <script type="text/javascript">
34288  */
34289  
34290 /**
34291  * @class Roo.MessageBox
34292  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34293  * Example usage:
34294  *<pre><code>
34295 // Basic alert:
34296 Roo.Msg.alert('Status', 'Changes saved successfully.');
34297
34298 // Prompt for user data:
34299 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34300     if (btn == 'ok'){
34301         // process text value...
34302     }
34303 });
34304
34305 // Show a dialog using config options:
34306 Roo.Msg.show({
34307    title:'Save Changes?',
34308    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34309    buttons: Roo.Msg.YESNOCANCEL,
34310    fn: processResult,
34311    animEl: 'elId'
34312 });
34313 </code></pre>
34314  * @static
34315  */
34316 Roo.MessageBox = function(){
34317     var dlg, opt, mask, waitTimer;
34318     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34319     var buttons, activeTextEl, bwidth;
34320
34321     // private
34322     var handleButton = function(button){
34323         dlg.hide();
34324         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34325     };
34326
34327     // private
34328     var handleHide = function(){
34329         if(opt && opt.cls){
34330             dlg.el.removeClass(opt.cls);
34331         }
34332         if(waitTimer){
34333             Roo.TaskMgr.stop(waitTimer);
34334             waitTimer = null;
34335         }
34336     };
34337
34338     // private
34339     var updateButtons = function(b){
34340         var width = 0;
34341         if(!b){
34342             buttons["ok"].hide();
34343             buttons["cancel"].hide();
34344             buttons["yes"].hide();
34345             buttons["no"].hide();
34346             dlg.footer.dom.style.display = 'none';
34347             return width;
34348         }
34349         dlg.footer.dom.style.display = '';
34350         for(var k in buttons){
34351             if(typeof buttons[k] != "function"){
34352                 if(b[k]){
34353                     buttons[k].show();
34354                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34355                     width += buttons[k].el.getWidth()+15;
34356                 }else{
34357                     buttons[k].hide();
34358                 }
34359             }
34360         }
34361         return width;
34362     };
34363
34364     // private
34365     var handleEsc = function(d, k, e){
34366         if(opt && opt.closable !== false){
34367             dlg.hide();
34368         }
34369         if(e){
34370             e.stopEvent();
34371         }
34372     };
34373
34374     return {
34375         /**
34376          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34377          * @return {Roo.BasicDialog} The BasicDialog element
34378          */
34379         getDialog : function(){
34380            if(!dlg){
34381                 dlg = new Roo.BasicDialog("x-msg-box", {
34382                     autoCreate : true,
34383                     shadow: true,
34384                     draggable: true,
34385                     resizable:false,
34386                     constraintoviewport:false,
34387                     fixedcenter:true,
34388                     collapsible : false,
34389                     shim:true,
34390                     modal: true,
34391                     width:400, height:100,
34392                     buttonAlign:"center",
34393                     closeClick : function(){
34394                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34395                             handleButton("no");
34396                         }else{
34397                             handleButton("cancel");
34398                         }
34399                     }
34400                 });
34401                 dlg.on("hide", handleHide);
34402                 mask = dlg.mask;
34403                 dlg.addKeyListener(27, handleEsc);
34404                 buttons = {};
34405                 var bt = this.buttonText;
34406                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34407                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34408                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34409                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34410                 bodyEl = dlg.body.createChild({
34411
34412                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
34413                 });
34414                 msgEl = bodyEl.dom.firstChild;
34415                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34416                 textboxEl.enableDisplayMode();
34417                 textboxEl.addKeyListener([10,13], function(){
34418                     if(dlg.isVisible() && opt && opt.buttons){
34419                         if(opt.buttons.ok){
34420                             handleButton("ok");
34421                         }else if(opt.buttons.yes){
34422                             handleButton("yes");
34423                         }
34424                     }
34425                 });
34426                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34427                 textareaEl.enableDisplayMode();
34428                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34429                 progressEl.enableDisplayMode();
34430                 var pf = progressEl.dom.firstChild;
34431                 if (pf) {
34432                     pp = Roo.get(pf.firstChild);
34433                     pp.setHeight(pf.offsetHeight);
34434                 }
34435                 
34436             }
34437             return dlg;
34438         },
34439
34440         /**
34441          * Updates the message box body text
34442          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34443          * the XHTML-compliant non-breaking space character '&amp;#160;')
34444          * @return {Roo.MessageBox} This message box
34445          */
34446         updateText : function(text){
34447             if(!dlg.isVisible() && !opt.width){
34448                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34449             }
34450             msgEl.innerHTML = text || '&#160;';
34451       
34452             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34453             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34454             var w = Math.max(
34455                     Math.min(opt.width || cw , this.maxWidth), 
34456                     Math.max(opt.minWidth || this.minWidth, bwidth)
34457             );
34458             if(opt.prompt){
34459                 activeTextEl.setWidth(w);
34460             }
34461             if(dlg.isVisible()){
34462                 dlg.fixedcenter = false;
34463             }
34464             // to big, make it scroll. = But as usual stupid IE does not support
34465             // !important..
34466             
34467             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34468                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34469                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34470             } else {
34471                 bodyEl.dom.style.height = '';
34472                 bodyEl.dom.style.overflowY = '';
34473             }
34474             if (cw > w) {
34475                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34476             } else {
34477                 bodyEl.dom.style.overflowX = '';
34478             }
34479             
34480             dlg.setContentSize(w, bodyEl.getHeight());
34481             if(dlg.isVisible()){
34482                 dlg.fixedcenter = true;
34483             }
34484             return this;
34485         },
34486
34487         /**
34488          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34489          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34490          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34491          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34492          * @return {Roo.MessageBox} This message box
34493          */
34494         updateProgress : function(value, text){
34495             if(text){
34496                 this.updateText(text);
34497             }
34498             if (pp) { // weird bug on my firefox - for some reason this is not defined
34499                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34500             }
34501             return this;
34502         },        
34503
34504         /**
34505          * Returns true if the message box is currently displayed
34506          * @return {Boolean} True if the message box is visible, else false
34507          */
34508         isVisible : function(){
34509             return dlg && dlg.isVisible();  
34510         },
34511
34512         /**
34513          * Hides the message box if it is displayed
34514          */
34515         hide : function(){
34516             if(this.isVisible()){
34517                 dlg.hide();
34518             }  
34519         },
34520
34521         /**
34522          * Displays a new message box, or reinitializes an existing message box, based on the config options
34523          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34524          * The following config object properties are supported:
34525          * <pre>
34526 Property    Type             Description
34527 ----------  ---------------  ------------------------------------------------------------------------------------
34528 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34529                                    closes (defaults to undefined)
34530 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34531                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34532 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34533                                    progress and wait dialogs will ignore this property and always hide the
34534                                    close button as they can only be closed programmatically.
34535 cls               String           A custom CSS class to apply to the message box element
34536 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34537                                    displayed (defaults to 75)
34538 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34539                                    function will be btn (the name of the button that was clicked, if applicable,
34540                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34541                                    Progress and wait dialogs will ignore this option since they do not respond to
34542                                    user actions and can only be closed programmatically, so any required function
34543                                    should be called by the same code after it closes the dialog.
34544 icon              String           A CSS class that provides a background image to be used as an icon for
34545                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34546 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34547 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34548 modal             Boolean          False to allow user interaction with the page while the message box is
34549                                    displayed (defaults to true)
34550 msg               String           A string that will replace the existing message box body text (defaults
34551                                    to the XHTML-compliant non-breaking space character '&#160;')
34552 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34553 progress          Boolean          True to display a progress bar (defaults to false)
34554 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34555 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34556 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34557 title             String           The title text
34558 value             String           The string value to set into the active textbox element if displayed
34559 wait              Boolean          True to display a progress bar (defaults to false)
34560 width             Number           The width of the dialog in pixels
34561 </pre>
34562          *
34563          * Example usage:
34564          * <pre><code>
34565 Roo.Msg.show({
34566    title: 'Address',
34567    msg: 'Please enter your address:',
34568    width: 300,
34569    buttons: Roo.MessageBox.OKCANCEL,
34570    multiline: true,
34571    fn: saveAddress,
34572    animEl: 'addAddressBtn'
34573 });
34574 </code></pre>
34575          * @param {Object} config Configuration options
34576          * @return {Roo.MessageBox} This message box
34577          */
34578         show : function(options)
34579         {
34580             
34581             // this causes nightmares if you show one dialog after another
34582             // especially on callbacks..
34583              
34584             if(this.isVisible()){
34585                 
34586                 this.hide();
34587                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34588                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34589                 Roo.log("New Dialog Message:" +  options.msg )
34590                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34591                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34592                 
34593             }
34594             var d = this.getDialog();
34595             opt = options;
34596             d.setTitle(opt.title || "&#160;");
34597             d.close.setDisplayed(opt.closable !== false);
34598             activeTextEl = textboxEl;
34599             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34600             if(opt.prompt){
34601                 if(opt.multiline){
34602                     textboxEl.hide();
34603                     textareaEl.show();
34604                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34605                         opt.multiline : this.defaultTextHeight);
34606                     activeTextEl = textareaEl;
34607                 }else{
34608                     textboxEl.show();
34609                     textareaEl.hide();
34610                 }
34611             }else{
34612                 textboxEl.hide();
34613                 textareaEl.hide();
34614             }
34615             progressEl.setDisplayed(opt.progress === true);
34616             this.updateProgress(0);
34617             activeTextEl.dom.value = opt.value || "";
34618             if(opt.prompt){
34619                 dlg.setDefaultButton(activeTextEl);
34620             }else{
34621                 var bs = opt.buttons;
34622                 var db = null;
34623                 if(bs && bs.ok){
34624                     db = buttons["ok"];
34625                 }else if(bs && bs.yes){
34626                     db = buttons["yes"];
34627                 }
34628                 dlg.setDefaultButton(db);
34629             }
34630             bwidth = updateButtons(opt.buttons);
34631             this.updateText(opt.msg);
34632             if(opt.cls){
34633                 d.el.addClass(opt.cls);
34634             }
34635             d.proxyDrag = opt.proxyDrag === true;
34636             d.modal = opt.modal !== false;
34637             d.mask = opt.modal !== false ? mask : false;
34638             if(!d.isVisible()){
34639                 // force it to the end of the z-index stack so it gets a cursor in FF
34640                 document.body.appendChild(dlg.el.dom);
34641                 d.animateTarget = null;
34642                 d.show(options.animEl);
34643             }
34644             return this;
34645         },
34646
34647         /**
34648          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34649          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34650          * and closing the message box when the process is complete.
34651          * @param {String} title The title bar text
34652          * @param {String} msg The message box body text
34653          * @return {Roo.MessageBox} This message box
34654          */
34655         progress : function(title, msg){
34656             this.show({
34657                 title : title,
34658                 msg : msg,
34659                 buttons: false,
34660                 progress:true,
34661                 closable:false,
34662                 minWidth: this.minProgressWidth,
34663                 modal : true
34664             });
34665             return this;
34666         },
34667
34668         /**
34669          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34670          * If a callback function is passed it will be called after the user clicks the button, and the
34671          * id of the button that was clicked will be passed as the only parameter to the callback
34672          * (could also be the top-right close button).
34673          * @param {String} title The title bar text
34674          * @param {String} msg The message box body text
34675          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34676          * @param {Object} scope (optional) The scope of the callback function
34677          * @return {Roo.MessageBox} This message box
34678          */
34679         alert : function(title, msg, fn, scope){
34680             this.show({
34681                 title : title,
34682                 msg : msg,
34683                 buttons: this.OK,
34684                 fn: fn,
34685                 scope : scope,
34686                 modal : true
34687             });
34688             return this;
34689         },
34690
34691         /**
34692          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
34693          * interaction while waiting for a long-running process to complete that does not have defined intervals.
34694          * You are responsible for closing the message box when the process is complete.
34695          * @param {String} msg The message box body text
34696          * @param {String} title (optional) The title bar text
34697          * @return {Roo.MessageBox} This message box
34698          */
34699         wait : function(msg, title){
34700             this.show({
34701                 title : title,
34702                 msg : msg,
34703                 buttons: false,
34704                 closable:false,
34705                 progress:true,
34706                 modal:true,
34707                 width:300,
34708                 wait:true
34709             });
34710             waitTimer = Roo.TaskMgr.start({
34711                 run: function(i){
34712                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
34713                 },
34714                 interval: 1000
34715             });
34716             return this;
34717         },
34718
34719         /**
34720          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
34721          * If a callback function is passed it will be called after the user clicks either button, and the id of the
34722          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
34723          * @param {String} title The title bar text
34724          * @param {String} msg The message box body text
34725          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34726          * @param {Object} scope (optional) The scope of the callback function
34727          * @return {Roo.MessageBox} This message box
34728          */
34729         confirm : function(title, msg, fn, scope){
34730             this.show({
34731                 title : title,
34732                 msg : msg,
34733                 buttons: this.YESNO,
34734                 fn: fn,
34735                 scope : scope,
34736                 modal : true
34737             });
34738             return this;
34739         },
34740
34741         /**
34742          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
34743          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
34744          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
34745          * (could also be the top-right close button) and the text that was entered will be passed as the two
34746          * parameters to the callback.
34747          * @param {String} title The title bar text
34748          * @param {String} msg The message box body text
34749          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34750          * @param {Object} scope (optional) The scope of the callback function
34751          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
34752          * property, or the height in pixels to create the textbox (defaults to false / single-line)
34753          * @return {Roo.MessageBox} This message box
34754          */
34755         prompt : function(title, msg, fn, scope, multiline){
34756             this.show({
34757                 title : title,
34758                 msg : msg,
34759                 buttons: this.OKCANCEL,
34760                 fn: fn,
34761                 minWidth:250,
34762                 scope : scope,
34763                 prompt:true,
34764                 multiline: multiline,
34765                 modal : true
34766             });
34767             return this;
34768         },
34769
34770         /**
34771          * Button config that displays a single OK button
34772          * @type Object
34773          */
34774         OK : {ok:true},
34775         /**
34776          * Button config that displays Yes and No buttons
34777          * @type Object
34778          */
34779         YESNO : {yes:true, no:true},
34780         /**
34781          * Button config that displays OK and Cancel buttons
34782          * @type Object
34783          */
34784         OKCANCEL : {ok:true, cancel:true},
34785         /**
34786          * Button config that displays Yes, No and Cancel buttons
34787          * @type Object
34788          */
34789         YESNOCANCEL : {yes:true, no:true, cancel:true},
34790
34791         /**
34792          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
34793          * @type Number
34794          */
34795         defaultTextHeight : 75,
34796         /**
34797          * The maximum width in pixels of the message box (defaults to 600)
34798          * @type Number
34799          */
34800         maxWidth : 600,
34801         /**
34802          * The minimum width in pixels of the message box (defaults to 100)
34803          * @type Number
34804          */
34805         minWidth : 100,
34806         /**
34807          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
34808          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
34809          * @type Number
34810          */
34811         minProgressWidth : 250,
34812         /**
34813          * An object containing the default button text strings that can be overriden for localized language support.
34814          * Supported properties are: ok, cancel, yes and no.
34815          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
34816          * @type Object
34817          */
34818         buttonText : {
34819             ok : "OK",
34820             cancel : "Cancel",
34821             yes : "Yes",
34822             no : "No"
34823         }
34824     };
34825 }();
34826
34827 /**
34828  * Shorthand for {@link Roo.MessageBox}
34829  */
34830 Roo.Msg = Roo.MessageBox;/*
34831  * Based on:
34832  * Ext JS Library 1.1.1
34833  * Copyright(c) 2006-2007, Ext JS, LLC.
34834  *
34835  * Originally Released Under LGPL - original licence link has changed is not relivant.
34836  *
34837  * Fork - LGPL
34838  * <script type="text/javascript">
34839  */
34840 /**
34841  * @class Roo.QuickTips
34842  * Provides attractive and customizable tooltips for any element.
34843  * @static
34844  */
34845 Roo.QuickTips = function(){
34846     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
34847     var ce, bd, xy, dd;
34848     var visible = false, disabled = true, inited = false;
34849     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
34850     
34851     var onOver = function(e){
34852         if(disabled){
34853             return;
34854         }
34855         var t = e.getTarget();
34856         if(!t || t.nodeType !== 1 || t == document || t == document.body){
34857             return;
34858         }
34859         if(ce && t == ce.el){
34860             clearTimeout(hideProc);
34861             return;
34862         }
34863         if(t && tagEls[t.id]){
34864             tagEls[t.id].el = t;
34865             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
34866             return;
34867         }
34868         var ttp, et = Roo.fly(t);
34869         var ns = cfg.namespace;
34870         if(tm.interceptTitles && t.title){
34871             ttp = t.title;
34872             t.qtip = ttp;
34873             t.removeAttribute("title");
34874             e.preventDefault();
34875         }else{
34876             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
34877         }
34878         if(ttp){
34879             showProc = show.defer(tm.showDelay, tm, [{
34880                 el: t, 
34881                 text: ttp.replace(/\\n/g,'<br/>'),
34882                 width: et.getAttributeNS(ns, cfg.width),
34883                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
34884                 title: et.getAttributeNS(ns, cfg.title),
34885                     cls: et.getAttributeNS(ns, cfg.cls)
34886             }]);
34887         }
34888     };
34889     
34890     var onOut = function(e){
34891         clearTimeout(showProc);
34892         var t = e.getTarget();
34893         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
34894             hideProc = setTimeout(hide, tm.hideDelay);
34895         }
34896     };
34897     
34898     var onMove = function(e){
34899         if(disabled){
34900             return;
34901         }
34902         xy = e.getXY();
34903         xy[1] += 18;
34904         if(tm.trackMouse && ce){
34905             el.setXY(xy);
34906         }
34907     };
34908     
34909     var onDown = function(e){
34910         clearTimeout(showProc);
34911         clearTimeout(hideProc);
34912         if(!e.within(el)){
34913             if(tm.hideOnClick){
34914                 hide();
34915                 tm.disable();
34916                 tm.enable.defer(100, tm);
34917             }
34918         }
34919     };
34920     
34921     var getPad = function(){
34922         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34923     };
34924
34925     var show = function(o){
34926         if(disabled){
34927             return;
34928         }
34929         clearTimeout(dismissProc);
34930         ce = o;
34931         if(removeCls){ // in case manually hidden
34932             el.removeClass(removeCls);
34933             removeCls = null;
34934         }
34935         if(ce.cls){
34936             el.addClass(ce.cls);
34937             removeCls = ce.cls;
34938         }
34939         if(ce.title){
34940             tipTitle.update(ce.title);
34941             tipTitle.show();
34942         }else{
34943             tipTitle.update('');
34944             tipTitle.hide();
34945         }
34946         el.dom.style.width  = tm.maxWidth+'px';
34947         //tipBody.dom.style.width = '';
34948         tipBodyText.update(o.text);
34949         var p = getPad(), w = ce.width;
34950         if(!w){
34951             var td = tipBodyText.dom;
34952             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34953             if(aw > tm.maxWidth){
34954                 w = tm.maxWidth;
34955             }else if(aw < tm.minWidth){
34956                 w = tm.minWidth;
34957             }else{
34958                 w = aw;
34959             }
34960         }
34961         //tipBody.setWidth(w);
34962         el.setWidth(parseInt(w, 10) + p);
34963         if(ce.autoHide === false){
34964             close.setDisplayed(true);
34965             if(dd){
34966                 dd.unlock();
34967             }
34968         }else{
34969             close.setDisplayed(false);
34970             if(dd){
34971                 dd.lock();
34972             }
34973         }
34974         if(xy){
34975             el.avoidY = xy[1]-18;
34976             el.setXY(xy);
34977         }
34978         if(tm.animate){
34979             el.setOpacity(.1);
34980             el.setStyle("visibility", "visible");
34981             el.fadeIn({callback: afterShow});
34982         }else{
34983             afterShow();
34984         }
34985     };
34986     
34987     var afterShow = function(){
34988         if(ce){
34989             el.show();
34990             esc.enable();
34991             if(tm.autoDismiss && ce.autoHide !== false){
34992                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34993             }
34994         }
34995     };
34996     
34997     var hide = function(noanim){
34998         clearTimeout(dismissProc);
34999         clearTimeout(hideProc);
35000         ce = null;
35001         if(el.isVisible()){
35002             esc.disable();
35003             if(noanim !== true && tm.animate){
35004                 el.fadeOut({callback: afterHide});
35005             }else{
35006                 afterHide();
35007             } 
35008         }
35009     };
35010     
35011     var afterHide = function(){
35012         el.hide();
35013         if(removeCls){
35014             el.removeClass(removeCls);
35015             removeCls = null;
35016         }
35017     };
35018     
35019     return {
35020         /**
35021         * @cfg {Number} minWidth
35022         * The minimum width of the quick tip (defaults to 40)
35023         */
35024        minWidth : 40,
35025         /**
35026         * @cfg {Number} maxWidth
35027         * The maximum width of the quick tip (defaults to 300)
35028         */
35029        maxWidth : 300,
35030         /**
35031         * @cfg {Boolean} interceptTitles
35032         * True to automatically use the element's DOM title value if available (defaults to false)
35033         */
35034        interceptTitles : false,
35035         /**
35036         * @cfg {Boolean} trackMouse
35037         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35038         */
35039        trackMouse : false,
35040         /**
35041         * @cfg {Boolean} hideOnClick
35042         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35043         */
35044        hideOnClick : true,
35045         /**
35046         * @cfg {Number} showDelay
35047         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35048         */
35049        showDelay : 500,
35050         /**
35051         * @cfg {Number} hideDelay
35052         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35053         */
35054        hideDelay : 200,
35055         /**
35056         * @cfg {Boolean} autoHide
35057         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35058         * Used in conjunction with hideDelay.
35059         */
35060        autoHide : true,
35061         /**
35062         * @cfg {Boolean}
35063         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35064         * (defaults to true).  Used in conjunction with autoDismissDelay.
35065         */
35066        autoDismiss : true,
35067         /**
35068         * @cfg {Number}
35069         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35070         */
35071        autoDismissDelay : 5000,
35072        /**
35073         * @cfg {Boolean} animate
35074         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35075         */
35076        animate : false,
35077
35078        /**
35079         * @cfg {String} title
35080         * Title text to display (defaults to '').  This can be any valid HTML markup.
35081         */
35082         title: '',
35083        /**
35084         * @cfg {String} text
35085         * Body text to display (defaults to '').  This can be any valid HTML markup.
35086         */
35087         text : '',
35088        /**
35089         * @cfg {String} cls
35090         * A CSS class to apply to the base quick tip element (defaults to '').
35091         */
35092         cls : '',
35093        /**
35094         * @cfg {Number} width
35095         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35096         * minWidth or maxWidth.
35097         */
35098         width : null,
35099
35100     /**
35101      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35102      * or display QuickTips in a page.
35103      */
35104        init : function(){
35105           tm = Roo.QuickTips;
35106           cfg = tm.tagConfig;
35107           if(!inited){
35108               if(!Roo.isReady){ // allow calling of init() before onReady
35109                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35110                   return;
35111               }
35112               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35113               el.fxDefaults = {stopFx: true};
35114               // maximum custom styling
35115               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
35116               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
35117               tipTitle = el.child('h3');
35118               tipTitle.enableDisplayMode("block");
35119               tipBody = el.child('div.x-tip-bd');
35120               tipBodyText = el.child('div.x-tip-bd-inner');
35121               //bdLeft = el.child('div.x-tip-bd-left');
35122               //bdRight = el.child('div.x-tip-bd-right');
35123               close = el.child('div.x-tip-close');
35124               close.enableDisplayMode("block");
35125               close.on("click", hide);
35126               var d = Roo.get(document);
35127               d.on("mousedown", onDown);
35128               d.on("mouseover", onOver);
35129               d.on("mouseout", onOut);
35130               d.on("mousemove", onMove);
35131               esc = d.addKeyListener(27, hide);
35132               esc.disable();
35133               if(Roo.dd.DD){
35134                   dd = el.initDD("default", null, {
35135                       onDrag : function(){
35136                           el.sync();  
35137                       }
35138                   });
35139                   dd.setHandleElId(tipTitle.id);
35140                   dd.lock();
35141               }
35142               inited = true;
35143           }
35144           this.enable(); 
35145        },
35146
35147     /**
35148      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35149      * are supported:
35150      * <pre>
35151 Property    Type                   Description
35152 ----------  ---------------------  ------------------------------------------------------------------------
35153 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35154      * </ul>
35155      * @param {Object} config The config object
35156      */
35157        register : function(config){
35158            var cs = config instanceof Array ? config : arguments;
35159            for(var i = 0, len = cs.length; i < len; i++) {
35160                var c = cs[i];
35161                var target = c.target;
35162                if(target){
35163                    if(target instanceof Array){
35164                        for(var j = 0, jlen = target.length; j < jlen; j++){
35165                            tagEls[target[j]] = c;
35166                        }
35167                    }else{
35168                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35169                    }
35170                }
35171            }
35172        },
35173
35174     /**
35175      * Removes this quick tip from its element and destroys it.
35176      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35177      */
35178        unregister : function(el){
35179            delete tagEls[Roo.id(el)];
35180        },
35181
35182     /**
35183      * Enable this quick tip.
35184      */
35185        enable : function(){
35186            if(inited && disabled){
35187                locks.pop();
35188                if(locks.length < 1){
35189                    disabled = false;
35190                }
35191            }
35192        },
35193
35194     /**
35195      * Disable this quick tip.
35196      */
35197        disable : function(){
35198           disabled = true;
35199           clearTimeout(showProc);
35200           clearTimeout(hideProc);
35201           clearTimeout(dismissProc);
35202           if(ce){
35203               hide(true);
35204           }
35205           locks.push(1);
35206        },
35207
35208     /**
35209      * Returns true if the quick tip is enabled, else false.
35210      */
35211        isEnabled : function(){
35212             return !disabled;
35213        },
35214
35215         // private
35216        tagConfig : {
35217            namespace : "roo", // was ext?? this may break..
35218            alt_namespace : "ext",
35219            attribute : "qtip",
35220            width : "width",
35221            target : "target",
35222            title : "qtitle",
35223            hide : "hide",
35224            cls : "qclass"
35225        }
35226    };
35227 }();
35228
35229 // backwards compat
35230 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35231  * Based on:
35232  * Ext JS Library 1.1.1
35233  * Copyright(c) 2006-2007, Ext JS, LLC.
35234  *
35235  * Originally Released Under LGPL - original licence link has changed is not relivant.
35236  *
35237  * Fork - LGPL
35238  * <script type="text/javascript">
35239  */
35240  
35241
35242 /**
35243  * @class Roo.tree.TreePanel
35244  * @extends Roo.data.Tree
35245  * @cfg {Roo.tree.TreeNode} root The root node
35246  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35247  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35248  * @cfg {Boolean} enableDD true to enable drag and drop
35249  * @cfg {Boolean} enableDrag true to enable just drag
35250  * @cfg {Boolean} enableDrop true to enable just drop
35251  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35252  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35253  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35254  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35255  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35256  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35257  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35258  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35259  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35260  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35261  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35262  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35263  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35264  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35265  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
35266  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
35267  * 
35268  * @constructor
35269  * @param {String/HTMLElement/Element} el The container element
35270  * @param {Object} config
35271  */
35272 Roo.tree.TreePanel = function(el, config){
35273     var root = false;
35274     var loader = false;
35275     if (config.root) {
35276         root = config.root;
35277         delete config.root;
35278     }
35279     if (config.loader) {
35280         loader = config.loader;
35281         delete config.loader;
35282     }
35283     
35284     Roo.apply(this, config);
35285     Roo.tree.TreePanel.superclass.constructor.call(this);
35286     this.el = Roo.get(el);
35287     this.el.addClass('x-tree');
35288     //console.log(root);
35289     if (root) {
35290         this.setRootNode( Roo.factory(root, Roo.tree));
35291     }
35292     if (loader) {
35293         this.loader = Roo.factory(loader, Roo.tree);
35294     }
35295    /**
35296     * Read-only. The id of the container element becomes this TreePanel's id.
35297     */
35298     this.id = this.el.id;
35299     this.addEvents({
35300         /**
35301         * @event beforeload
35302         * Fires before a node is loaded, return false to cancel
35303         * @param {Node} node The node being loaded
35304         */
35305         "beforeload" : true,
35306         /**
35307         * @event load
35308         * Fires when a node is loaded
35309         * @param {Node} node The node that was loaded
35310         */
35311         "load" : true,
35312         /**
35313         * @event textchange
35314         * Fires when the text for a node is changed
35315         * @param {Node} node The node
35316         * @param {String} text The new text
35317         * @param {String} oldText The old text
35318         */
35319         "textchange" : true,
35320         /**
35321         * @event beforeexpand
35322         * Fires before a node is expanded, return false to cancel.
35323         * @param {Node} node The node
35324         * @param {Boolean} deep
35325         * @param {Boolean} anim
35326         */
35327         "beforeexpand" : true,
35328         /**
35329         * @event beforecollapse
35330         * Fires before a node is collapsed, return false to cancel.
35331         * @param {Node} node The node
35332         * @param {Boolean} deep
35333         * @param {Boolean} anim
35334         */
35335         "beforecollapse" : true,
35336         /**
35337         * @event expand
35338         * Fires when a node is expanded
35339         * @param {Node} node The node
35340         */
35341         "expand" : true,
35342         /**
35343         * @event disabledchange
35344         * Fires when the disabled status of a node changes
35345         * @param {Node} node The node
35346         * @param {Boolean} disabled
35347         */
35348         "disabledchange" : true,
35349         /**
35350         * @event collapse
35351         * Fires when a node is collapsed
35352         * @param {Node} node The node
35353         */
35354         "collapse" : true,
35355         /**
35356         * @event beforeclick
35357         * Fires before click processing on a node. Return false to cancel the default action.
35358         * @param {Node} node The node
35359         * @param {Roo.EventObject} e The event object
35360         */
35361         "beforeclick":true,
35362         /**
35363         * @event checkchange
35364         * Fires when a node with a checkbox's checked property changes
35365         * @param {Node} this This node
35366         * @param {Boolean} checked
35367         */
35368         "checkchange":true,
35369         /**
35370         * @event click
35371         * Fires when a node is clicked
35372         * @param {Node} node The node
35373         * @param {Roo.EventObject} e The event object
35374         */
35375         "click":true,
35376         /**
35377         * @event dblclick
35378         * Fires when a node is double clicked
35379         * @param {Node} node The node
35380         * @param {Roo.EventObject} e The event object
35381         */
35382         "dblclick":true,
35383         /**
35384         * @event contextmenu
35385         * Fires when a node is right clicked
35386         * @param {Node} node The node
35387         * @param {Roo.EventObject} e The event object
35388         */
35389         "contextmenu":true,
35390         /**
35391         * @event beforechildrenrendered
35392         * Fires right before the child nodes for a node are rendered
35393         * @param {Node} node The node
35394         */
35395         "beforechildrenrendered":true,
35396         /**
35397         * @event startdrag
35398         * Fires when a node starts being dragged
35399         * @param {Roo.tree.TreePanel} this
35400         * @param {Roo.tree.TreeNode} node
35401         * @param {event} e The raw browser event
35402         */ 
35403        "startdrag" : true,
35404        /**
35405         * @event enddrag
35406         * Fires when a drag operation is complete
35407         * @param {Roo.tree.TreePanel} this
35408         * @param {Roo.tree.TreeNode} node
35409         * @param {event} e The raw browser event
35410         */
35411        "enddrag" : true,
35412        /**
35413         * @event dragdrop
35414         * Fires when a dragged node is dropped on a valid DD target
35415         * @param {Roo.tree.TreePanel} this
35416         * @param {Roo.tree.TreeNode} node
35417         * @param {DD} dd The dd it was dropped on
35418         * @param {event} e The raw browser event
35419         */
35420        "dragdrop" : true,
35421        /**
35422         * @event beforenodedrop
35423         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35424         * passed to handlers has the following properties:<br />
35425         * <ul style="padding:5px;padding-left:16px;">
35426         * <li>tree - The TreePanel</li>
35427         * <li>target - The node being targeted for the drop</li>
35428         * <li>data - The drag data from the drag source</li>
35429         * <li>point - The point of the drop - append, above or below</li>
35430         * <li>source - The drag source</li>
35431         * <li>rawEvent - Raw mouse event</li>
35432         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35433         * to be inserted by setting them on this object.</li>
35434         * <li>cancel - Set this to true to cancel the drop.</li>
35435         * </ul>
35436         * @param {Object} dropEvent
35437         */
35438        "beforenodedrop" : true,
35439        /**
35440         * @event nodedrop
35441         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35442         * passed to handlers has the following properties:<br />
35443         * <ul style="padding:5px;padding-left:16px;">
35444         * <li>tree - The TreePanel</li>
35445         * <li>target - The node being targeted for the drop</li>
35446         * <li>data - The drag data from the drag source</li>
35447         * <li>point - The point of the drop - append, above or below</li>
35448         * <li>source - The drag source</li>
35449         * <li>rawEvent - Raw mouse event</li>
35450         * <li>dropNode - Dropped node(s).</li>
35451         * </ul>
35452         * @param {Object} dropEvent
35453         */
35454        "nodedrop" : true,
35455         /**
35456         * @event nodedragover
35457         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35458         * passed to handlers has the following properties:<br />
35459         * <ul style="padding:5px;padding-left:16px;">
35460         * <li>tree - The TreePanel</li>
35461         * <li>target - The node being targeted for the drop</li>
35462         * <li>data - The drag data from the drag source</li>
35463         * <li>point - The point of the drop - append, above or below</li>
35464         * <li>source - The drag source</li>
35465         * <li>rawEvent - Raw mouse event</li>
35466         * <li>dropNode - Drop node(s) provided by the source.</li>
35467         * <li>cancel - Set this to true to signal drop not allowed.</li>
35468         * </ul>
35469         * @param {Object} dragOverEvent
35470         */
35471        "nodedragover" : true,
35472        /**
35473         * @event appendnode
35474         * Fires when append node to the tree
35475         * @param {Roo.tree.TreePanel} this
35476         * @param {Roo.tree.TreeNode} node
35477         * @param {Number} index The index of the newly appended node
35478         */
35479        "appendnode" : true
35480         
35481     });
35482     if(this.singleExpand){
35483        this.on("beforeexpand", this.restrictExpand, this);
35484     }
35485     if (this.editor) {
35486         this.editor.tree = this;
35487         this.editor = Roo.factory(this.editor, Roo.tree);
35488     }
35489     
35490     if (this.selModel) {
35491         this.selModel = Roo.factory(this.selModel, Roo.tree);
35492     }
35493    
35494 };
35495 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35496     rootVisible : true,
35497     animate: Roo.enableFx,
35498     lines : true,
35499     enableDD : false,
35500     hlDrop : Roo.enableFx,
35501   
35502     renderer: false,
35503     
35504     rendererTip: false,
35505     // private
35506     restrictExpand : function(node){
35507         var p = node.parentNode;
35508         if(p){
35509             if(p.expandedChild && p.expandedChild.parentNode == p){
35510                 p.expandedChild.collapse();
35511             }
35512             p.expandedChild = node;
35513         }
35514     },
35515
35516     // private override
35517     setRootNode : function(node){
35518         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35519         if(!this.rootVisible){
35520             node.ui = new Roo.tree.RootTreeNodeUI(node);
35521         }
35522         return node;
35523     },
35524
35525     /**
35526      * Returns the container element for this TreePanel
35527      */
35528     getEl : function(){
35529         return this.el;
35530     },
35531
35532     /**
35533      * Returns the default TreeLoader for this TreePanel
35534      */
35535     getLoader : function(){
35536         return this.loader;
35537     },
35538
35539     /**
35540      * Expand all nodes
35541      */
35542     expandAll : function(){
35543         this.root.expand(true);
35544     },
35545
35546     /**
35547      * Collapse all nodes
35548      */
35549     collapseAll : function(){
35550         this.root.collapse(true);
35551     },
35552
35553     /**
35554      * Returns the selection model used by this TreePanel
35555      */
35556     getSelectionModel : function(){
35557         if(!this.selModel){
35558             this.selModel = new Roo.tree.DefaultSelectionModel();
35559         }
35560         return this.selModel;
35561     },
35562
35563     /**
35564      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35565      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35566      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35567      * @return {Array}
35568      */
35569     getChecked : function(a, startNode){
35570         startNode = startNode || this.root;
35571         var r = [];
35572         var f = function(){
35573             if(this.attributes.checked){
35574                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35575             }
35576         }
35577         startNode.cascade(f);
35578         return r;
35579     },
35580
35581     /**
35582      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35583      * @param {String} path
35584      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35585      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35586      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35587      */
35588     expandPath : function(path, attr, callback){
35589         attr = attr || "id";
35590         var keys = path.split(this.pathSeparator);
35591         var curNode = this.root;
35592         if(curNode.attributes[attr] != keys[1]){ // invalid root
35593             if(callback){
35594                 callback(false, null);
35595             }
35596             return;
35597         }
35598         var index = 1;
35599         var f = function(){
35600             if(++index == keys.length){
35601                 if(callback){
35602                     callback(true, curNode);
35603                 }
35604                 return;
35605             }
35606             var c = curNode.findChild(attr, keys[index]);
35607             if(!c){
35608                 if(callback){
35609                     callback(false, curNode);
35610                 }
35611                 return;
35612             }
35613             curNode = c;
35614             c.expand(false, false, f);
35615         };
35616         curNode.expand(false, false, f);
35617     },
35618
35619     /**
35620      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35621      * @param {String} path
35622      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35623      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35624      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35625      */
35626     selectPath : function(path, attr, callback){
35627         attr = attr || "id";
35628         var keys = path.split(this.pathSeparator);
35629         var v = keys.pop();
35630         if(keys.length > 0){
35631             var f = function(success, node){
35632                 if(success && node){
35633                     var n = node.findChild(attr, v);
35634                     if(n){
35635                         n.select();
35636                         if(callback){
35637                             callback(true, n);
35638                         }
35639                     }else if(callback){
35640                         callback(false, n);
35641                     }
35642                 }else{
35643                     if(callback){
35644                         callback(false, n);
35645                     }
35646                 }
35647             };
35648             this.expandPath(keys.join(this.pathSeparator), attr, f);
35649         }else{
35650             this.root.select();
35651             if(callback){
35652                 callback(true, this.root);
35653             }
35654         }
35655     },
35656
35657     getTreeEl : function(){
35658         return this.el;
35659     },
35660
35661     /**
35662      * Trigger rendering of this TreePanel
35663      */
35664     render : function(){
35665         if (this.innerCt) {
35666             return this; // stop it rendering more than once!!
35667         }
35668         
35669         this.innerCt = this.el.createChild({tag:"ul",
35670                cls:"x-tree-root-ct " +
35671                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35672
35673         if(this.containerScroll){
35674             Roo.dd.ScrollManager.register(this.el);
35675         }
35676         if((this.enableDD || this.enableDrop) && !this.dropZone){
35677            /**
35678             * The dropZone used by this tree if drop is enabled
35679             * @type Roo.tree.TreeDropZone
35680             */
35681              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35682                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35683            });
35684         }
35685         if((this.enableDD || this.enableDrag) && !this.dragZone){
35686            /**
35687             * The dragZone used by this tree if drag is enabled
35688             * @type Roo.tree.TreeDragZone
35689             */
35690             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35691                ddGroup: this.ddGroup || "TreeDD",
35692                scroll: this.ddScroll
35693            });
35694         }
35695         this.getSelectionModel().init(this);
35696         if (!this.root) {
35697             Roo.log("ROOT not set in tree");
35698             return this;
35699         }
35700         this.root.render();
35701         if(!this.rootVisible){
35702             this.root.renderChildren();
35703         }
35704         return this;
35705     }
35706 });/*
35707  * Based on:
35708  * Ext JS Library 1.1.1
35709  * Copyright(c) 2006-2007, Ext JS, LLC.
35710  *
35711  * Originally Released Under LGPL - original licence link has changed is not relivant.
35712  *
35713  * Fork - LGPL
35714  * <script type="text/javascript">
35715  */
35716  
35717
35718 /**
35719  * @class Roo.tree.DefaultSelectionModel
35720  * @extends Roo.util.Observable
35721  * The default single selection for a TreePanel.
35722  * @param {Object} cfg Configuration
35723  */
35724 Roo.tree.DefaultSelectionModel = function(cfg){
35725    this.selNode = null;
35726    
35727    
35728    
35729    this.addEvents({
35730        /**
35731         * @event selectionchange
35732         * Fires when the selected node changes
35733         * @param {DefaultSelectionModel} this
35734         * @param {TreeNode} node the new selection
35735         */
35736        "selectionchange" : true,
35737
35738        /**
35739         * @event beforeselect
35740         * Fires before the selected node changes, return false to cancel the change
35741         * @param {DefaultSelectionModel} this
35742         * @param {TreeNode} node the new selection
35743         * @param {TreeNode} node the old selection
35744         */
35745        "beforeselect" : true
35746    });
35747    
35748     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
35749 };
35750
35751 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
35752     init : function(tree){
35753         this.tree = tree;
35754         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35755         tree.on("click", this.onNodeClick, this);
35756     },
35757     
35758     onNodeClick : function(node, e){
35759         if (e.ctrlKey && this.selNode == node)  {
35760             this.unselect(node);
35761             return;
35762         }
35763         this.select(node);
35764     },
35765     
35766     /**
35767      * Select a node.
35768      * @param {TreeNode} node The node to select
35769      * @return {TreeNode} The selected node
35770      */
35771     select : function(node){
35772         var last = this.selNode;
35773         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
35774             if(last){
35775                 last.ui.onSelectedChange(false);
35776             }
35777             this.selNode = node;
35778             node.ui.onSelectedChange(true);
35779             this.fireEvent("selectionchange", this, node, last);
35780         }
35781         return node;
35782     },
35783     
35784     /**
35785      * Deselect a node.
35786      * @param {TreeNode} node The node to unselect
35787      */
35788     unselect : function(node){
35789         if(this.selNode == node){
35790             this.clearSelections();
35791         }    
35792     },
35793     
35794     /**
35795      * Clear all selections
35796      */
35797     clearSelections : function(){
35798         var n = this.selNode;
35799         if(n){
35800             n.ui.onSelectedChange(false);
35801             this.selNode = null;
35802             this.fireEvent("selectionchange", this, null);
35803         }
35804         return n;
35805     },
35806     
35807     /**
35808      * Get the selected node
35809      * @return {TreeNode} The selected node
35810      */
35811     getSelectedNode : function(){
35812         return this.selNode;    
35813     },
35814     
35815     /**
35816      * Returns true if the node is selected
35817      * @param {TreeNode} node The node to check
35818      * @return {Boolean}
35819      */
35820     isSelected : function(node){
35821         return this.selNode == node;  
35822     },
35823
35824     /**
35825      * Selects the node above the selected node in the tree, intelligently walking the nodes
35826      * @return TreeNode The new selection
35827      */
35828     selectPrevious : function(){
35829         var s = this.selNode || this.lastSelNode;
35830         if(!s){
35831             return null;
35832         }
35833         var ps = s.previousSibling;
35834         if(ps){
35835             if(!ps.isExpanded() || ps.childNodes.length < 1){
35836                 return this.select(ps);
35837             } else{
35838                 var lc = ps.lastChild;
35839                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
35840                     lc = lc.lastChild;
35841                 }
35842                 return this.select(lc);
35843             }
35844         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
35845             return this.select(s.parentNode);
35846         }
35847         return null;
35848     },
35849
35850     /**
35851      * Selects the node above the selected node in the tree, intelligently walking the nodes
35852      * @return TreeNode The new selection
35853      */
35854     selectNext : function(){
35855         var s = this.selNode || this.lastSelNode;
35856         if(!s){
35857             return null;
35858         }
35859         if(s.firstChild && s.isExpanded()){
35860              return this.select(s.firstChild);
35861          }else if(s.nextSibling){
35862              return this.select(s.nextSibling);
35863          }else if(s.parentNode){
35864             var newS = null;
35865             s.parentNode.bubble(function(){
35866                 if(this.nextSibling){
35867                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
35868                     return false;
35869                 }
35870             });
35871             return newS;
35872          }
35873         return null;
35874     },
35875
35876     onKeyDown : function(e){
35877         var s = this.selNode || this.lastSelNode;
35878         // undesirable, but required
35879         var sm = this;
35880         if(!s){
35881             return;
35882         }
35883         var k = e.getKey();
35884         switch(k){
35885              case e.DOWN:
35886                  e.stopEvent();
35887                  this.selectNext();
35888              break;
35889              case e.UP:
35890                  e.stopEvent();
35891                  this.selectPrevious();
35892              break;
35893              case e.RIGHT:
35894                  e.preventDefault();
35895                  if(s.hasChildNodes()){
35896                      if(!s.isExpanded()){
35897                          s.expand();
35898                      }else if(s.firstChild){
35899                          this.select(s.firstChild, e);
35900                      }
35901                  }
35902              break;
35903              case e.LEFT:
35904                  e.preventDefault();
35905                  if(s.hasChildNodes() && s.isExpanded()){
35906                      s.collapse();
35907                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
35908                      this.select(s.parentNode, e);
35909                  }
35910              break;
35911         };
35912     }
35913 });
35914
35915 /**
35916  * @class Roo.tree.MultiSelectionModel
35917  * @extends Roo.util.Observable
35918  * Multi selection for a TreePanel.
35919  * @param {Object} cfg Configuration
35920  */
35921 Roo.tree.MultiSelectionModel = function(){
35922    this.selNodes = [];
35923    this.selMap = {};
35924    this.addEvents({
35925        /**
35926         * @event selectionchange
35927         * Fires when the selected nodes change
35928         * @param {MultiSelectionModel} this
35929         * @param {Array} nodes Array of the selected nodes
35930         */
35931        "selectionchange" : true
35932    });
35933    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35934    
35935 };
35936
35937 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35938     init : function(tree){
35939         this.tree = tree;
35940         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35941         tree.on("click", this.onNodeClick, this);
35942     },
35943     
35944     onNodeClick : function(node, e){
35945         this.select(node, e, e.ctrlKey);
35946     },
35947     
35948     /**
35949      * Select a node.
35950      * @param {TreeNode} node The node to select
35951      * @param {EventObject} e (optional) An event associated with the selection
35952      * @param {Boolean} keepExisting True to retain existing selections
35953      * @return {TreeNode} The selected node
35954      */
35955     select : function(node, e, keepExisting){
35956         if(keepExisting !== true){
35957             this.clearSelections(true);
35958         }
35959         if(this.isSelected(node)){
35960             this.lastSelNode = node;
35961             return node;
35962         }
35963         this.selNodes.push(node);
35964         this.selMap[node.id] = node;
35965         this.lastSelNode = node;
35966         node.ui.onSelectedChange(true);
35967         this.fireEvent("selectionchange", this, this.selNodes);
35968         return node;
35969     },
35970     
35971     /**
35972      * Deselect a node.
35973      * @param {TreeNode} node The node to unselect
35974      */
35975     unselect : function(node){
35976         if(this.selMap[node.id]){
35977             node.ui.onSelectedChange(false);
35978             var sn = this.selNodes;
35979             var index = -1;
35980             if(sn.indexOf){
35981                 index = sn.indexOf(node);
35982             }else{
35983                 for(var i = 0, len = sn.length; i < len; i++){
35984                     if(sn[i] == node){
35985                         index = i;
35986                         break;
35987                     }
35988                 }
35989             }
35990             if(index != -1){
35991                 this.selNodes.splice(index, 1);
35992             }
35993             delete this.selMap[node.id];
35994             this.fireEvent("selectionchange", this, this.selNodes);
35995         }
35996     },
35997     
35998     /**
35999      * Clear all selections
36000      */
36001     clearSelections : function(suppressEvent){
36002         var sn = this.selNodes;
36003         if(sn.length > 0){
36004             for(var i = 0, len = sn.length; i < len; i++){
36005                 sn[i].ui.onSelectedChange(false);
36006             }
36007             this.selNodes = [];
36008             this.selMap = {};
36009             if(suppressEvent !== true){
36010                 this.fireEvent("selectionchange", this, this.selNodes);
36011             }
36012         }
36013     },
36014     
36015     /**
36016      * Returns true if the node is selected
36017      * @param {TreeNode} node The node to check
36018      * @return {Boolean}
36019      */
36020     isSelected : function(node){
36021         return this.selMap[node.id] ? true : false;  
36022     },
36023     
36024     /**
36025      * Returns an array of the selected nodes
36026      * @return {Array}
36027      */
36028     getSelectedNodes : function(){
36029         return this.selNodes;    
36030     },
36031
36032     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36033
36034     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36035
36036     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36037 });/*
36038  * Based on:
36039  * Ext JS Library 1.1.1
36040  * Copyright(c) 2006-2007, Ext JS, LLC.
36041  *
36042  * Originally Released Under LGPL - original licence link has changed is not relivant.
36043  *
36044  * Fork - LGPL
36045  * <script type="text/javascript">
36046  */
36047  
36048 /**
36049  * @class Roo.tree.TreeNode
36050  * @extends Roo.data.Node
36051  * @cfg {String} text The text for this node
36052  * @cfg {Boolean} expanded true to start the node expanded
36053  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36054  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36055  * @cfg {Boolean} disabled true to start the node disabled
36056  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36057  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36058  * @cfg {String} cls A css class to be added to the node
36059  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36060  * @cfg {String} href URL of the link used for the node (defaults to #)
36061  * @cfg {String} hrefTarget target frame for the link
36062  * @cfg {String} qtip An Ext QuickTip for the node
36063  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36064  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36065  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36066  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36067  * (defaults to undefined with no checkbox rendered)
36068  * @constructor
36069  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36070  */
36071 Roo.tree.TreeNode = function(attributes){
36072     attributes = attributes || {};
36073     if(typeof attributes == "string"){
36074         attributes = {text: attributes};
36075     }
36076     this.childrenRendered = false;
36077     this.rendered = false;
36078     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36079     this.expanded = attributes.expanded === true;
36080     this.isTarget = attributes.isTarget !== false;
36081     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36082     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36083
36084     /**
36085      * Read-only. The text for this node. To change it use setText().
36086      * @type String
36087      */
36088     this.text = attributes.text;
36089     /**
36090      * True if this node is disabled.
36091      * @type Boolean
36092      */
36093     this.disabled = attributes.disabled === true;
36094
36095     this.addEvents({
36096         /**
36097         * @event textchange
36098         * Fires when the text for this node is changed
36099         * @param {Node} this This node
36100         * @param {String} text The new text
36101         * @param {String} oldText The old text
36102         */
36103         "textchange" : true,
36104         /**
36105         * @event beforeexpand
36106         * Fires before this node is expanded, return false to cancel.
36107         * @param {Node} this This node
36108         * @param {Boolean} deep
36109         * @param {Boolean} anim
36110         */
36111         "beforeexpand" : true,
36112         /**
36113         * @event beforecollapse
36114         * Fires before this node is collapsed, return false to cancel.
36115         * @param {Node} this This node
36116         * @param {Boolean} deep
36117         * @param {Boolean} anim
36118         */
36119         "beforecollapse" : true,
36120         /**
36121         * @event expand
36122         * Fires when this node is expanded
36123         * @param {Node} this This node
36124         */
36125         "expand" : true,
36126         /**
36127         * @event disabledchange
36128         * Fires when the disabled status of this node changes
36129         * @param {Node} this This node
36130         * @param {Boolean} disabled
36131         */
36132         "disabledchange" : true,
36133         /**
36134         * @event collapse
36135         * Fires when this node is collapsed
36136         * @param {Node} this This node
36137         */
36138         "collapse" : true,
36139         /**
36140         * @event beforeclick
36141         * Fires before click processing. Return false to cancel the default action.
36142         * @param {Node} this This node
36143         * @param {Roo.EventObject} e The event object
36144         */
36145         "beforeclick":true,
36146         /**
36147         * @event checkchange
36148         * Fires when a node with a checkbox's checked property changes
36149         * @param {Node} this This node
36150         * @param {Boolean} checked
36151         */
36152         "checkchange":true,
36153         /**
36154         * @event click
36155         * Fires when this node is clicked
36156         * @param {Node} this This node
36157         * @param {Roo.EventObject} e The event object
36158         */
36159         "click":true,
36160         /**
36161         * @event dblclick
36162         * Fires when this node is double clicked
36163         * @param {Node} this This node
36164         * @param {Roo.EventObject} e The event object
36165         */
36166         "dblclick":true,
36167         /**
36168         * @event contextmenu
36169         * Fires when this node is right clicked
36170         * @param {Node} this This node
36171         * @param {Roo.EventObject} e The event object
36172         */
36173         "contextmenu":true,
36174         /**
36175         * @event beforechildrenrendered
36176         * Fires right before the child nodes for this node are rendered
36177         * @param {Node} this This node
36178         */
36179         "beforechildrenrendered":true
36180     });
36181
36182     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36183
36184     /**
36185      * Read-only. The UI for this node
36186      * @type TreeNodeUI
36187      */
36188     this.ui = new uiClass(this);
36189     
36190     // finally support items[]
36191     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36192         return;
36193     }
36194     
36195     
36196     Roo.each(this.attributes.items, function(c) {
36197         this.appendChild(Roo.factory(c,Roo.Tree));
36198     }, this);
36199     delete this.attributes.items;
36200     
36201     
36202     
36203 };
36204 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36205     preventHScroll: true,
36206     /**
36207      * Returns true if this node is expanded
36208      * @return {Boolean}
36209      */
36210     isExpanded : function(){
36211         return this.expanded;
36212     },
36213
36214     /**
36215      * Returns the UI object for this node
36216      * @return {TreeNodeUI}
36217      */
36218     getUI : function(){
36219         return this.ui;
36220     },
36221
36222     // private override
36223     setFirstChild : function(node){
36224         var of = this.firstChild;
36225         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36226         if(this.childrenRendered && of && node != of){
36227             of.renderIndent(true, true);
36228         }
36229         if(this.rendered){
36230             this.renderIndent(true, true);
36231         }
36232     },
36233
36234     // private override
36235     setLastChild : function(node){
36236         var ol = this.lastChild;
36237         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36238         if(this.childrenRendered && ol && node != ol){
36239             ol.renderIndent(true, true);
36240         }
36241         if(this.rendered){
36242             this.renderIndent(true, true);
36243         }
36244     },
36245
36246     // these methods are overridden to provide lazy rendering support
36247     // private override
36248     appendChild : function()
36249     {
36250         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36251         if(node && this.childrenRendered){
36252             node.render();
36253         }
36254         this.ui.updateExpandIcon();
36255         return node;
36256     },
36257
36258     // private override
36259     removeChild : function(node){
36260         this.ownerTree.getSelectionModel().unselect(node);
36261         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36262         // if it's been rendered remove dom node
36263         if(this.childrenRendered){
36264             node.ui.remove();
36265         }
36266         if(this.childNodes.length < 1){
36267             this.collapse(false, false);
36268         }else{
36269             this.ui.updateExpandIcon();
36270         }
36271         if(!this.firstChild) {
36272             this.childrenRendered = false;
36273         }
36274         return node;
36275     },
36276
36277     // private override
36278     insertBefore : function(node, refNode){
36279         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36280         if(newNode && refNode && this.childrenRendered){
36281             node.render();
36282         }
36283         this.ui.updateExpandIcon();
36284         return newNode;
36285     },
36286
36287     /**
36288      * Sets the text for this node
36289      * @param {String} text
36290      */
36291     setText : function(text){
36292         var oldText = this.text;
36293         this.text = text;
36294         this.attributes.text = text;
36295         if(this.rendered){ // event without subscribing
36296             this.ui.onTextChange(this, text, oldText);
36297         }
36298         this.fireEvent("textchange", this, text, oldText);
36299     },
36300
36301     /**
36302      * Triggers selection of this node
36303      */
36304     select : function(){
36305         this.getOwnerTree().getSelectionModel().select(this);
36306     },
36307
36308     /**
36309      * Triggers deselection of this node
36310      */
36311     unselect : function(){
36312         this.getOwnerTree().getSelectionModel().unselect(this);
36313     },
36314
36315     /**
36316      * Returns true if this node is selected
36317      * @return {Boolean}
36318      */
36319     isSelected : function(){
36320         return this.getOwnerTree().getSelectionModel().isSelected(this);
36321     },
36322
36323     /**
36324      * Expand this node.
36325      * @param {Boolean} deep (optional) True to expand all children as well
36326      * @param {Boolean} anim (optional) false to cancel the default animation
36327      * @param {Function} callback (optional) A callback to be called when
36328      * expanding this node completes (does not wait for deep expand to complete).
36329      * Called with 1 parameter, this node.
36330      */
36331     expand : function(deep, anim, callback){
36332         if(!this.expanded){
36333             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36334                 return;
36335             }
36336             if(!this.childrenRendered){
36337                 this.renderChildren();
36338             }
36339             this.expanded = true;
36340             
36341             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36342                 this.ui.animExpand(function(){
36343                     this.fireEvent("expand", this);
36344                     if(typeof callback == "function"){
36345                         callback(this);
36346                     }
36347                     if(deep === true){
36348                         this.expandChildNodes(true);
36349                     }
36350                 }.createDelegate(this));
36351                 return;
36352             }else{
36353                 this.ui.expand();
36354                 this.fireEvent("expand", this);
36355                 if(typeof callback == "function"){
36356                     callback(this);
36357                 }
36358             }
36359         }else{
36360            if(typeof callback == "function"){
36361                callback(this);
36362            }
36363         }
36364         if(deep === true){
36365             this.expandChildNodes(true);
36366         }
36367     },
36368
36369     isHiddenRoot : function(){
36370         return this.isRoot && !this.getOwnerTree().rootVisible;
36371     },
36372
36373     /**
36374      * Collapse this node.
36375      * @param {Boolean} deep (optional) True to collapse all children as well
36376      * @param {Boolean} anim (optional) false to cancel the default animation
36377      */
36378     collapse : function(deep, anim){
36379         if(this.expanded && !this.isHiddenRoot()){
36380             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36381                 return;
36382             }
36383             this.expanded = false;
36384             if((this.getOwnerTree().animate && anim !== false) || anim){
36385                 this.ui.animCollapse(function(){
36386                     this.fireEvent("collapse", this);
36387                     if(deep === true){
36388                         this.collapseChildNodes(true);
36389                     }
36390                 }.createDelegate(this));
36391                 return;
36392             }else{
36393                 this.ui.collapse();
36394                 this.fireEvent("collapse", this);
36395             }
36396         }
36397         if(deep === true){
36398             var cs = this.childNodes;
36399             for(var i = 0, len = cs.length; i < len; i++) {
36400                 cs[i].collapse(true, false);
36401             }
36402         }
36403     },
36404
36405     // private
36406     delayedExpand : function(delay){
36407         if(!this.expandProcId){
36408             this.expandProcId = this.expand.defer(delay, this);
36409         }
36410     },
36411
36412     // private
36413     cancelExpand : function(){
36414         if(this.expandProcId){
36415             clearTimeout(this.expandProcId);
36416         }
36417         this.expandProcId = false;
36418     },
36419
36420     /**
36421      * Toggles expanded/collapsed state of the node
36422      */
36423     toggle : function(){
36424         if(this.expanded){
36425             this.collapse();
36426         }else{
36427             this.expand();
36428         }
36429     },
36430
36431     /**
36432      * Ensures all parent nodes are expanded
36433      */
36434     ensureVisible : function(callback){
36435         var tree = this.getOwnerTree();
36436         tree.expandPath(this.parentNode.getPath(), false, function(){
36437             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36438             Roo.callback(callback);
36439         }.createDelegate(this));
36440     },
36441
36442     /**
36443      * Expand all child nodes
36444      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36445      */
36446     expandChildNodes : function(deep){
36447         var cs = this.childNodes;
36448         for(var i = 0, len = cs.length; i < len; i++) {
36449                 cs[i].expand(deep);
36450         }
36451     },
36452
36453     /**
36454      * Collapse all child nodes
36455      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36456      */
36457     collapseChildNodes : function(deep){
36458         var cs = this.childNodes;
36459         for(var i = 0, len = cs.length; i < len; i++) {
36460                 cs[i].collapse(deep);
36461         }
36462     },
36463
36464     /**
36465      * Disables this node
36466      */
36467     disable : function(){
36468         this.disabled = true;
36469         this.unselect();
36470         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36471             this.ui.onDisableChange(this, true);
36472         }
36473         this.fireEvent("disabledchange", this, true);
36474     },
36475
36476     /**
36477      * Enables this node
36478      */
36479     enable : function(){
36480         this.disabled = false;
36481         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36482             this.ui.onDisableChange(this, false);
36483         }
36484         this.fireEvent("disabledchange", this, false);
36485     },
36486
36487     // private
36488     renderChildren : function(suppressEvent){
36489         if(suppressEvent !== false){
36490             this.fireEvent("beforechildrenrendered", this);
36491         }
36492         var cs = this.childNodes;
36493         for(var i = 0, len = cs.length; i < len; i++){
36494             cs[i].render(true);
36495         }
36496         this.childrenRendered = true;
36497     },
36498
36499     // private
36500     sort : function(fn, scope){
36501         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36502         if(this.childrenRendered){
36503             var cs = this.childNodes;
36504             for(var i = 0, len = cs.length; i < len; i++){
36505                 cs[i].render(true);
36506             }
36507         }
36508     },
36509
36510     // private
36511     render : function(bulkRender){
36512         this.ui.render(bulkRender);
36513         if(!this.rendered){
36514             this.rendered = true;
36515             if(this.expanded){
36516                 this.expanded = false;
36517                 this.expand(false, false);
36518             }
36519         }
36520     },
36521
36522     // private
36523     renderIndent : function(deep, refresh){
36524         if(refresh){
36525             this.ui.childIndent = null;
36526         }
36527         this.ui.renderIndent();
36528         if(deep === true && this.childrenRendered){
36529             var cs = this.childNodes;
36530             for(var i = 0, len = cs.length; i < len; i++){
36531                 cs[i].renderIndent(true, refresh);
36532             }
36533         }
36534     }
36535 });/*
36536  * Based on:
36537  * Ext JS Library 1.1.1
36538  * Copyright(c) 2006-2007, Ext JS, LLC.
36539  *
36540  * Originally Released Under LGPL - original licence link has changed is not relivant.
36541  *
36542  * Fork - LGPL
36543  * <script type="text/javascript">
36544  */
36545  
36546 /**
36547  * @class Roo.tree.AsyncTreeNode
36548  * @extends Roo.tree.TreeNode
36549  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36550  * @constructor
36551  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36552  */
36553  Roo.tree.AsyncTreeNode = function(config){
36554     this.loaded = false;
36555     this.loading = false;
36556     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36557     /**
36558     * @event beforeload
36559     * Fires before this node is loaded, return false to cancel
36560     * @param {Node} this This node
36561     */
36562     this.addEvents({'beforeload':true, 'load': true});
36563     /**
36564     * @event load
36565     * Fires when this node is loaded
36566     * @param {Node} this This node
36567     */
36568     /**
36569      * The loader used by this node (defaults to using the tree's defined loader)
36570      * @type TreeLoader
36571      * @property loader
36572      */
36573 };
36574 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36575     expand : function(deep, anim, callback){
36576         if(this.loading){ // if an async load is already running, waiting til it's done
36577             var timer;
36578             var f = function(){
36579                 if(!this.loading){ // done loading
36580                     clearInterval(timer);
36581                     this.expand(deep, anim, callback);
36582                 }
36583             }.createDelegate(this);
36584             timer = setInterval(f, 200);
36585             return;
36586         }
36587         if(!this.loaded){
36588             if(this.fireEvent("beforeload", this) === false){
36589                 return;
36590             }
36591             this.loading = true;
36592             this.ui.beforeLoad(this);
36593             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36594             if(loader){
36595                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36596                 return;
36597             }
36598         }
36599         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36600     },
36601     
36602     /**
36603      * Returns true if this node is currently loading
36604      * @return {Boolean}
36605      */
36606     isLoading : function(){
36607         return this.loading;  
36608     },
36609     
36610     loadComplete : function(deep, anim, callback){
36611         this.loading = false;
36612         this.loaded = true;
36613         this.ui.afterLoad(this);
36614         this.fireEvent("load", this);
36615         this.expand(deep, anim, callback);
36616     },
36617     
36618     /**
36619      * Returns true if this node has been loaded
36620      * @return {Boolean}
36621      */
36622     isLoaded : function(){
36623         return this.loaded;
36624     },
36625     
36626     hasChildNodes : function(){
36627         if(!this.isLeaf() && !this.loaded){
36628             return true;
36629         }else{
36630             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36631         }
36632     },
36633
36634     /**
36635      * Trigger a reload for this node
36636      * @param {Function} callback
36637      */
36638     reload : function(callback){
36639         this.collapse(false, false);
36640         while(this.firstChild){
36641             this.removeChild(this.firstChild);
36642         }
36643         this.childrenRendered = false;
36644         this.loaded = false;
36645         if(this.isHiddenRoot()){
36646             this.expanded = false;
36647         }
36648         this.expand(false, false, callback);
36649     }
36650 });/*
36651  * Based on:
36652  * Ext JS Library 1.1.1
36653  * Copyright(c) 2006-2007, Ext JS, LLC.
36654  *
36655  * Originally Released Under LGPL - original licence link has changed is not relivant.
36656  *
36657  * Fork - LGPL
36658  * <script type="text/javascript">
36659  */
36660  
36661 /**
36662  * @class Roo.tree.TreeNodeUI
36663  * @constructor
36664  * @param {Object} node The node to render
36665  * The TreeNode UI implementation is separate from the
36666  * tree implementation. Unless you are customizing the tree UI,
36667  * you should never have to use this directly.
36668  */
36669 Roo.tree.TreeNodeUI = function(node){
36670     this.node = node;
36671     this.rendered = false;
36672     this.animating = false;
36673     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36674 };
36675
36676 Roo.tree.TreeNodeUI.prototype = {
36677     removeChild : function(node){
36678         if(this.rendered){
36679             this.ctNode.removeChild(node.ui.getEl());
36680         }
36681     },
36682
36683     beforeLoad : function(){
36684          this.addClass("x-tree-node-loading");
36685     },
36686
36687     afterLoad : function(){
36688          this.removeClass("x-tree-node-loading");
36689     },
36690
36691     onTextChange : function(node, text, oldText){
36692         if(this.rendered){
36693             this.textNode.innerHTML = text;
36694         }
36695     },
36696
36697     onDisableChange : function(node, state){
36698         this.disabled = state;
36699         if(state){
36700             this.addClass("x-tree-node-disabled");
36701         }else{
36702             this.removeClass("x-tree-node-disabled");
36703         }
36704     },
36705
36706     onSelectedChange : function(state){
36707         if(state){
36708             this.focus();
36709             this.addClass("x-tree-selected");
36710         }else{
36711             //this.blur();
36712             this.removeClass("x-tree-selected");
36713         }
36714     },
36715
36716     onMove : function(tree, node, oldParent, newParent, index, refNode){
36717         this.childIndent = null;
36718         if(this.rendered){
36719             var targetNode = newParent.ui.getContainer();
36720             if(!targetNode){//target not rendered
36721                 this.holder = document.createElement("div");
36722                 this.holder.appendChild(this.wrap);
36723                 return;
36724             }
36725             var insertBefore = refNode ? refNode.ui.getEl() : null;
36726             if(insertBefore){
36727                 targetNode.insertBefore(this.wrap, insertBefore);
36728             }else{
36729                 targetNode.appendChild(this.wrap);
36730             }
36731             this.node.renderIndent(true);
36732         }
36733     },
36734
36735     addClass : function(cls){
36736         if(this.elNode){
36737             Roo.fly(this.elNode).addClass(cls);
36738         }
36739     },
36740
36741     removeClass : function(cls){
36742         if(this.elNode){
36743             Roo.fly(this.elNode).removeClass(cls);
36744         }
36745     },
36746
36747     remove : function(){
36748         if(this.rendered){
36749             this.holder = document.createElement("div");
36750             this.holder.appendChild(this.wrap);
36751         }
36752     },
36753
36754     fireEvent : function(){
36755         return this.node.fireEvent.apply(this.node, arguments);
36756     },
36757
36758     initEvents : function(){
36759         this.node.on("move", this.onMove, this);
36760         var E = Roo.EventManager;
36761         var a = this.anchor;
36762
36763         var el = Roo.fly(a, '_treeui');
36764
36765         if(Roo.isOpera){ // opera render bug ignores the CSS
36766             el.setStyle("text-decoration", "none");
36767         }
36768
36769         el.on("click", this.onClick, this);
36770         el.on("dblclick", this.onDblClick, this);
36771
36772         if(this.checkbox){
36773             Roo.EventManager.on(this.checkbox,
36774                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
36775         }
36776
36777         el.on("contextmenu", this.onContextMenu, this);
36778
36779         var icon = Roo.fly(this.iconNode);
36780         icon.on("click", this.onClick, this);
36781         icon.on("dblclick", this.onDblClick, this);
36782         icon.on("contextmenu", this.onContextMenu, this);
36783         E.on(this.ecNode, "click", this.ecClick, this, true);
36784
36785         if(this.node.disabled){
36786             this.addClass("x-tree-node-disabled");
36787         }
36788         if(this.node.hidden){
36789             this.addClass("x-tree-node-disabled");
36790         }
36791         var ot = this.node.getOwnerTree();
36792         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
36793         if(dd && (!this.node.isRoot || ot.rootVisible)){
36794             Roo.dd.Registry.register(this.elNode, {
36795                 node: this.node,
36796                 handles: this.getDDHandles(),
36797                 isHandle: false
36798             });
36799         }
36800     },
36801
36802     getDDHandles : function(){
36803         return [this.iconNode, this.textNode];
36804     },
36805
36806     hide : function(){
36807         if(this.rendered){
36808             this.wrap.style.display = "none";
36809         }
36810     },
36811
36812     show : function(){
36813         if(this.rendered){
36814             this.wrap.style.display = "";
36815         }
36816     },
36817
36818     onContextMenu : function(e){
36819         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
36820             e.preventDefault();
36821             this.focus();
36822             this.fireEvent("contextmenu", this.node, e);
36823         }
36824     },
36825
36826     onClick : function(e){
36827         if(this.dropping){
36828             e.stopEvent();
36829             return;
36830         }
36831         if(this.fireEvent("beforeclick", this.node, e) !== false){
36832             if(!this.disabled && this.node.attributes.href){
36833                 this.fireEvent("click", this.node, e);
36834                 return;
36835             }
36836             e.preventDefault();
36837             if(this.disabled){
36838                 return;
36839             }
36840
36841             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
36842                 this.node.toggle();
36843             }
36844
36845             this.fireEvent("click", this.node, e);
36846         }else{
36847             e.stopEvent();
36848         }
36849     },
36850
36851     onDblClick : function(e){
36852         e.preventDefault();
36853         if(this.disabled){
36854             return;
36855         }
36856         if(this.checkbox){
36857             this.toggleCheck();
36858         }
36859         if(!this.animating && this.node.hasChildNodes()){
36860             this.node.toggle();
36861         }
36862         this.fireEvent("dblclick", this.node, e);
36863     },
36864
36865     onCheckChange : function(){
36866         var checked = this.checkbox.checked;
36867         this.node.attributes.checked = checked;
36868         this.fireEvent('checkchange', this.node, checked);
36869     },
36870
36871     ecClick : function(e){
36872         if(!this.animating && this.node.hasChildNodes()){
36873             this.node.toggle();
36874         }
36875     },
36876
36877     startDrop : function(){
36878         this.dropping = true;
36879     },
36880
36881     // delayed drop so the click event doesn't get fired on a drop
36882     endDrop : function(){
36883        setTimeout(function(){
36884            this.dropping = false;
36885        }.createDelegate(this), 50);
36886     },
36887
36888     expand : function(){
36889         this.updateExpandIcon();
36890         this.ctNode.style.display = "";
36891     },
36892
36893     focus : function(){
36894         if(!this.node.preventHScroll){
36895             try{this.anchor.focus();
36896             }catch(e){}
36897         }else if(!Roo.isIE){
36898             try{
36899                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
36900                 var l = noscroll.scrollLeft;
36901                 this.anchor.focus();
36902                 noscroll.scrollLeft = l;
36903             }catch(e){}
36904         }
36905     },
36906
36907     toggleCheck : function(value){
36908         var cb = this.checkbox;
36909         if(cb){
36910             cb.checked = (value === undefined ? !cb.checked : value);
36911         }
36912     },
36913
36914     blur : function(){
36915         try{
36916             this.anchor.blur();
36917         }catch(e){}
36918     },
36919
36920     animExpand : function(callback){
36921         var ct = Roo.get(this.ctNode);
36922         ct.stopFx();
36923         if(!this.node.hasChildNodes()){
36924             this.updateExpandIcon();
36925             this.ctNode.style.display = "";
36926             Roo.callback(callback);
36927             return;
36928         }
36929         this.animating = true;
36930         this.updateExpandIcon();
36931
36932         ct.slideIn('t', {
36933            callback : function(){
36934                this.animating = false;
36935                Roo.callback(callback);
36936             },
36937             scope: this,
36938             duration: this.node.ownerTree.duration || .25
36939         });
36940     },
36941
36942     highlight : function(){
36943         var tree = this.node.getOwnerTree();
36944         Roo.fly(this.wrap).highlight(
36945             tree.hlColor || "C3DAF9",
36946             {endColor: tree.hlBaseColor}
36947         );
36948     },
36949
36950     collapse : function(){
36951         this.updateExpandIcon();
36952         this.ctNode.style.display = "none";
36953     },
36954
36955     animCollapse : function(callback){
36956         var ct = Roo.get(this.ctNode);
36957         ct.enableDisplayMode('block');
36958         ct.stopFx();
36959
36960         this.animating = true;
36961         this.updateExpandIcon();
36962
36963         ct.slideOut('t', {
36964             callback : function(){
36965                this.animating = false;
36966                Roo.callback(callback);
36967             },
36968             scope: this,
36969             duration: this.node.ownerTree.duration || .25
36970         });
36971     },
36972
36973     getContainer : function(){
36974         return this.ctNode;
36975     },
36976
36977     getEl : function(){
36978         return this.wrap;
36979     },
36980
36981     appendDDGhost : function(ghostNode){
36982         ghostNode.appendChild(this.elNode.cloneNode(true));
36983     },
36984
36985     getDDRepairXY : function(){
36986         return Roo.lib.Dom.getXY(this.iconNode);
36987     },
36988
36989     onRender : function(){
36990         this.render();
36991     },
36992
36993     render : function(bulkRender){
36994         var n = this.node, a = n.attributes;
36995         var targetNode = n.parentNode ?
36996               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36997
36998         if(!this.rendered){
36999             this.rendered = true;
37000
37001             this.renderElements(n, a, targetNode, bulkRender);
37002
37003             if(a.qtip){
37004                if(this.textNode.setAttributeNS){
37005                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
37006                    if(a.qtipTitle){
37007                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
37008                    }
37009                }else{
37010                    this.textNode.setAttribute("ext:qtip", a.qtip);
37011                    if(a.qtipTitle){
37012                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37013                    }
37014                }
37015             }else if(a.qtipCfg){
37016                 a.qtipCfg.target = Roo.id(this.textNode);
37017                 Roo.QuickTips.register(a.qtipCfg);
37018             }
37019             this.initEvents();
37020             if(!this.node.expanded){
37021                 this.updateExpandIcon();
37022             }
37023         }else{
37024             if(bulkRender === true) {
37025                 targetNode.appendChild(this.wrap);
37026             }
37027         }
37028     },
37029
37030     renderElements : function(n, a, targetNode, bulkRender)
37031     {
37032         // add some indent caching, this helps performance when rendering a large tree
37033         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37034         var t = n.getOwnerTree();
37035         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37036         if (typeof(n.attributes.html) != 'undefined') {
37037             txt = n.attributes.html;
37038         }
37039         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37040         var cb = typeof a.checked == 'boolean';
37041         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37042         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37043             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37044             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37045             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37046             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37047             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37048              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37049                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37050             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37051             "</li>"];
37052
37053         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37054             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37055                                 n.nextSibling.ui.getEl(), buf.join(""));
37056         }else{
37057             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37058         }
37059
37060         this.elNode = this.wrap.childNodes[0];
37061         this.ctNode = this.wrap.childNodes[1];
37062         var cs = this.elNode.childNodes;
37063         this.indentNode = cs[0];
37064         this.ecNode = cs[1];
37065         this.iconNode = cs[2];
37066         var index = 3;
37067         if(cb){
37068             this.checkbox = cs[3];
37069             index++;
37070         }
37071         this.anchor = cs[index];
37072         this.textNode = cs[index].firstChild;
37073     },
37074
37075     getAnchor : function(){
37076         return this.anchor;
37077     },
37078
37079     getTextEl : function(){
37080         return this.textNode;
37081     },
37082
37083     getIconEl : function(){
37084         return this.iconNode;
37085     },
37086
37087     isChecked : function(){
37088         return this.checkbox ? this.checkbox.checked : false;
37089     },
37090
37091     updateExpandIcon : function(){
37092         if(this.rendered){
37093             var n = this.node, c1, c2;
37094             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37095             var hasChild = n.hasChildNodes();
37096             if(hasChild){
37097                 if(n.expanded){
37098                     cls += "-minus";
37099                     c1 = "x-tree-node-collapsed";
37100                     c2 = "x-tree-node-expanded";
37101                 }else{
37102                     cls += "-plus";
37103                     c1 = "x-tree-node-expanded";
37104                     c2 = "x-tree-node-collapsed";
37105                 }
37106                 if(this.wasLeaf){
37107                     this.removeClass("x-tree-node-leaf");
37108                     this.wasLeaf = false;
37109                 }
37110                 if(this.c1 != c1 || this.c2 != c2){
37111                     Roo.fly(this.elNode).replaceClass(c1, c2);
37112                     this.c1 = c1; this.c2 = c2;
37113                 }
37114             }else{
37115                 // this changes non-leafs into leafs if they have no children.
37116                 // it's not very rational behaviour..
37117                 
37118                 if(!this.wasLeaf && this.node.leaf){
37119                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37120                     delete this.c1;
37121                     delete this.c2;
37122                     this.wasLeaf = true;
37123                 }
37124             }
37125             var ecc = "x-tree-ec-icon "+cls;
37126             if(this.ecc != ecc){
37127                 this.ecNode.className = ecc;
37128                 this.ecc = ecc;
37129             }
37130         }
37131     },
37132
37133     getChildIndent : function(){
37134         if(!this.childIndent){
37135             var buf = [];
37136             var p = this.node;
37137             while(p){
37138                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37139                     if(!p.isLast()) {
37140                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37141                     } else {
37142                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37143                     }
37144                 }
37145                 p = p.parentNode;
37146             }
37147             this.childIndent = buf.join("");
37148         }
37149         return this.childIndent;
37150     },
37151
37152     renderIndent : function(){
37153         if(this.rendered){
37154             var indent = "";
37155             var p = this.node.parentNode;
37156             if(p){
37157                 indent = p.ui.getChildIndent();
37158             }
37159             if(this.indentMarkup != indent){ // don't rerender if not required
37160                 this.indentNode.innerHTML = indent;
37161                 this.indentMarkup = indent;
37162             }
37163             this.updateExpandIcon();
37164         }
37165     }
37166 };
37167
37168 Roo.tree.RootTreeNodeUI = function(){
37169     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37170 };
37171 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37172     render : function(){
37173         if(!this.rendered){
37174             var targetNode = this.node.ownerTree.innerCt.dom;
37175             this.node.expanded = true;
37176             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37177             this.wrap = this.ctNode = targetNode.firstChild;
37178         }
37179     },
37180     collapse : function(){
37181     },
37182     expand : function(){
37183     }
37184 });/*
37185  * Based on:
37186  * Ext JS Library 1.1.1
37187  * Copyright(c) 2006-2007, Ext JS, LLC.
37188  *
37189  * Originally Released Under LGPL - original licence link has changed is not relivant.
37190  *
37191  * Fork - LGPL
37192  * <script type="text/javascript">
37193  */
37194 /**
37195  * @class Roo.tree.TreeLoader
37196  * @extends Roo.util.Observable
37197  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37198  * nodes from a specified URL. The response must be a javascript Array definition
37199  * who's elements are node definition objects. eg:
37200  * <pre><code>
37201 {  success : true,
37202    data :      [
37203    
37204     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37205     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37206     ]
37207 }
37208
37209
37210 </code></pre>
37211  * <br><br>
37212  * The old style respose with just an array is still supported, but not recommended.
37213  * <br><br>
37214  *
37215  * A server request is sent, and child nodes are loaded only when a node is expanded.
37216  * The loading node's id is passed to the server under the parameter name "node" to
37217  * enable the server to produce the correct child nodes.
37218  * <br><br>
37219  * To pass extra parameters, an event handler may be attached to the "beforeload"
37220  * event, and the parameters specified in the TreeLoader's baseParams property:
37221  * <pre><code>
37222     myTreeLoader.on("beforeload", function(treeLoader, node) {
37223         this.baseParams.category = node.attributes.category;
37224     }, this);
37225     
37226 </code></pre>
37227  *
37228  * This would pass an HTTP parameter called "category" to the server containing
37229  * the value of the Node's "category" attribute.
37230  * @constructor
37231  * Creates a new Treeloader.
37232  * @param {Object} config A config object containing config properties.
37233  */
37234 Roo.tree.TreeLoader = function(config){
37235     this.baseParams = {};
37236     this.requestMethod = "POST";
37237     Roo.apply(this, config);
37238
37239     this.addEvents({
37240     
37241         /**
37242          * @event beforeload
37243          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37244          * @param {Object} This TreeLoader object.
37245          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37246          * @param {Object} callback The callback function specified in the {@link #load} call.
37247          */
37248         beforeload : true,
37249         /**
37250          * @event load
37251          * Fires when the node has been successfuly loaded.
37252          * @param {Object} This TreeLoader object.
37253          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37254          * @param {Object} response The response object containing the data from the server.
37255          */
37256         load : true,
37257         /**
37258          * @event loadexception
37259          * Fires if the network request failed.
37260          * @param {Object} This TreeLoader object.
37261          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37262          * @param {Object} response The response object containing the data from the server.
37263          */
37264         loadexception : true,
37265         /**
37266          * @event create
37267          * Fires before a node is created, enabling you to return custom Node types 
37268          * @param {Object} This TreeLoader object.
37269          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37270          */
37271         create : true
37272     });
37273
37274     Roo.tree.TreeLoader.superclass.constructor.call(this);
37275 };
37276
37277 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37278     /**
37279     * @cfg {String} dataUrl The URL from which to request a Json string which
37280     * specifies an array of node definition object representing the child nodes
37281     * to be loaded.
37282     */
37283     /**
37284     * @cfg {String} requestMethod either GET or POST
37285     * defaults to POST (due to BC)
37286     * to be loaded.
37287     */
37288     /**
37289     * @cfg {Object} baseParams (optional) An object containing properties which
37290     * specify HTTP parameters to be passed to each request for child nodes.
37291     */
37292     /**
37293     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37294     * created by this loader. If the attributes sent by the server have an attribute in this object,
37295     * they take priority.
37296     */
37297     /**
37298     * @cfg {Object} uiProviders (optional) An object containing properties which
37299     * 
37300     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37301     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37302     * <i>uiProvider</i> attribute of a returned child node is a string rather
37303     * than a reference to a TreeNodeUI implementation, this that string value
37304     * is used as a property name in the uiProviders object. You can define the provider named
37305     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37306     */
37307     uiProviders : {},
37308
37309     /**
37310     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37311     * child nodes before loading.
37312     */
37313     clearOnLoad : true,
37314
37315     /**
37316     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37317     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37318     * Grid query { data : [ .....] }
37319     */
37320     
37321     root : false,
37322      /**
37323     * @cfg {String} queryParam (optional) 
37324     * Name of the query as it will be passed on the querystring (defaults to 'node')
37325     * eg. the request will be ?node=[id]
37326     */
37327     
37328     
37329     queryParam: false,
37330     
37331     /**
37332      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37333      * This is called automatically when a node is expanded, but may be used to reload
37334      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37335      * @param {Roo.tree.TreeNode} node
37336      * @param {Function} callback
37337      */
37338     load : function(node, callback){
37339         if(this.clearOnLoad){
37340             while(node.firstChild){
37341                 node.removeChild(node.firstChild);
37342             }
37343         }
37344         if(node.attributes.children){ // preloaded json children
37345             var cs = node.attributes.children;
37346             for(var i = 0, len = cs.length; i < len; i++){
37347                 node.appendChild(this.createNode(cs[i]));
37348             }
37349             if(typeof callback == "function"){
37350                 callback();
37351             }
37352         }else if(this.dataUrl){
37353             this.requestData(node, callback);
37354         }
37355     },
37356
37357     getParams: function(node){
37358         var buf = [], bp = this.baseParams;
37359         for(var key in bp){
37360             if(typeof bp[key] != "function"){
37361                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37362             }
37363         }
37364         var n = this.queryParam === false ? 'node' : this.queryParam;
37365         buf.push(n + "=", encodeURIComponent(node.id));
37366         return buf.join("");
37367     },
37368
37369     requestData : function(node, callback){
37370         if(this.fireEvent("beforeload", this, node, callback) !== false){
37371             this.transId = Roo.Ajax.request({
37372                 method:this.requestMethod,
37373                 url: this.dataUrl||this.url,
37374                 success: this.handleResponse,
37375                 failure: this.handleFailure,
37376                 scope: this,
37377                 argument: {callback: callback, node: node},
37378                 params: this.getParams(node)
37379             });
37380         }else{
37381             // if the load is cancelled, make sure we notify
37382             // the node that we are done
37383             if(typeof callback == "function"){
37384                 callback();
37385             }
37386         }
37387     },
37388
37389     isLoading : function(){
37390         return this.transId ? true : false;
37391     },
37392
37393     abort : function(){
37394         if(this.isLoading()){
37395             Roo.Ajax.abort(this.transId);
37396         }
37397     },
37398
37399     // private
37400     createNode : function(attr)
37401     {
37402         // apply baseAttrs, nice idea Corey!
37403         if(this.baseAttrs){
37404             Roo.applyIf(attr, this.baseAttrs);
37405         }
37406         if(this.applyLoader !== false){
37407             attr.loader = this;
37408         }
37409         // uiProvider = depreciated..
37410         
37411         if(typeof(attr.uiProvider) == 'string'){
37412            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37413                 /**  eval:var:attr */ eval(attr.uiProvider);
37414         }
37415         if(typeof(this.uiProviders['default']) != 'undefined') {
37416             attr.uiProvider = this.uiProviders['default'];
37417         }
37418         
37419         this.fireEvent('create', this, attr);
37420         
37421         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37422         return(attr.leaf ?
37423                         new Roo.tree.TreeNode(attr) :
37424                         new Roo.tree.AsyncTreeNode(attr));
37425     },
37426
37427     processResponse : function(response, node, callback)
37428     {
37429         var json = response.responseText;
37430         try {
37431             
37432             var o = Roo.decode(json);
37433             
37434             if (this.root === false && typeof(o.success) != undefined) {
37435                 this.root = 'data'; // the default behaviour for list like data..
37436                 }
37437                 
37438             if (this.root !== false &&  !o.success) {
37439                 // it's a failure condition.
37440                 var a = response.argument;
37441                 this.fireEvent("loadexception", this, a.node, response);
37442                 Roo.log("Load failed - should have a handler really");
37443                 return;
37444             }
37445             
37446             
37447             
37448             if (this.root !== false) {
37449                  o = o[this.root];
37450             }
37451             
37452             for(var i = 0, len = o.length; i < len; i++){
37453                 var n = this.createNode(o[i]);
37454                 if(n){
37455                     node.appendChild(n);
37456                 }
37457             }
37458             if(typeof callback == "function"){
37459                 callback(this, node);
37460             }
37461         }catch(e){
37462             this.handleFailure(response);
37463         }
37464     },
37465
37466     handleResponse : function(response){
37467         this.transId = false;
37468         var a = response.argument;
37469         this.processResponse(response, a.node, a.callback);
37470         this.fireEvent("load", this, a.node, response);
37471     },
37472
37473     handleFailure : function(response)
37474     {
37475         // should handle failure better..
37476         this.transId = false;
37477         var a = response.argument;
37478         this.fireEvent("loadexception", this, a.node, response);
37479         if(typeof a.callback == "function"){
37480             a.callback(this, a.node);
37481         }
37482     }
37483 });/*
37484  * Based on:
37485  * Ext JS Library 1.1.1
37486  * Copyright(c) 2006-2007, Ext JS, LLC.
37487  *
37488  * Originally Released Under LGPL - original licence link has changed is not relivant.
37489  *
37490  * Fork - LGPL
37491  * <script type="text/javascript">
37492  */
37493
37494 /**
37495 * @class Roo.tree.TreeFilter
37496 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37497 * @param {TreePanel} tree
37498 * @param {Object} config (optional)
37499  */
37500 Roo.tree.TreeFilter = function(tree, config){
37501     this.tree = tree;
37502     this.filtered = {};
37503     Roo.apply(this, config);
37504 };
37505
37506 Roo.tree.TreeFilter.prototype = {
37507     clearBlank:false,
37508     reverse:false,
37509     autoClear:false,
37510     remove:false,
37511
37512      /**
37513      * Filter the data by a specific attribute.
37514      * @param {String/RegExp} value Either string that the attribute value
37515      * should start with or a RegExp to test against the attribute
37516      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37517      * @param {TreeNode} startNode (optional) The node to start the filter at.
37518      */
37519     filter : function(value, attr, startNode){
37520         attr = attr || "text";
37521         var f;
37522         if(typeof value == "string"){
37523             var vlen = value.length;
37524             // auto clear empty filter
37525             if(vlen == 0 && this.clearBlank){
37526                 this.clear();
37527                 return;
37528             }
37529             value = value.toLowerCase();
37530             f = function(n){
37531                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37532             };
37533         }else if(value.exec){ // regex?
37534             f = function(n){
37535                 return value.test(n.attributes[attr]);
37536             };
37537         }else{
37538             throw 'Illegal filter type, must be string or regex';
37539         }
37540         this.filterBy(f, null, startNode);
37541         },
37542
37543     /**
37544      * Filter by a function. The passed function will be called with each
37545      * node in the tree (or from the startNode). If the function returns true, the node is kept
37546      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37547      * @param {Function} fn The filter function
37548      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37549      */
37550     filterBy : function(fn, scope, startNode){
37551         startNode = startNode || this.tree.root;
37552         if(this.autoClear){
37553             this.clear();
37554         }
37555         var af = this.filtered, rv = this.reverse;
37556         var f = function(n){
37557             if(n == startNode){
37558                 return true;
37559             }
37560             if(af[n.id]){
37561                 return false;
37562             }
37563             var m = fn.call(scope || n, n);
37564             if(!m || rv){
37565                 af[n.id] = n;
37566                 n.ui.hide();
37567                 return false;
37568             }
37569             return true;
37570         };
37571         startNode.cascade(f);
37572         if(this.remove){
37573            for(var id in af){
37574                if(typeof id != "function"){
37575                    var n = af[id];
37576                    if(n && n.parentNode){
37577                        n.parentNode.removeChild(n);
37578                    }
37579                }
37580            }
37581         }
37582     },
37583
37584     /**
37585      * Clears the current filter. Note: with the "remove" option
37586      * set a filter cannot be cleared.
37587      */
37588     clear : function(){
37589         var t = this.tree;
37590         var af = this.filtered;
37591         for(var id in af){
37592             if(typeof id != "function"){
37593                 var n = af[id];
37594                 if(n){
37595                     n.ui.show();
37596                 }
37597             }
37598         }
37599         this.filtered = {};
37600     }
37601 };
37602 /*
37603  * Based on:
37604  * Ext JS Library 1.1.1
37605  * Copyright(c) 2006-2007, Ext JS, LLC.
37606  *
37607  * Originally Released Under LGPL - original licence link has changed is not relivant.
37608  *
37609  * Fork - LGPL
37610  * <script type="text/javascript">
37611  */
37612  
37613
37614 /**
37615  * @class Roo.tree.TreeSorter
37616  * Provides sorting of nodes in a TreePanel
37617  * 
37618  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37619  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37620  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37621  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37622  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37623  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37624  * @constructor
37625  * @param {TreePanel} tree
37626  * @param {Object} config
37627  */
37628 Roo.tree.TreeSorter = function(tree, config){
37629     Roo.apply(this, config);
37630     tree.on("beforechildrenrendered", this.doSort, this);
37631     tree.on("append", this.updateSort, this);
37632     tree.on("insert", this.updateSort, this);
37633     
37634     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37635     var p = this.property || "text";
37636     var sortType = this.sortType;
37637     var fs = this.folderSort;
37638     var cs = this.caseSensitive === true;
37639     var leafAttr = this.leafAttr || 'leaf';
37640
37641     this.sortFn = function(n1, n2){
37642         if(fs){
37643             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37644                 return 1;
37645             }
37646             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37647                 return -1;
37648             }
37649         }
37650         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37651         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37652         if(v1 < v2){
37653                         return dsc ? +1 : -1;
37654                 }else if(v1 > v2){
37655                         return dsc ? -1 : +1;
37656         }else{
37657                 return 0;
37658         }
37659     };
37660 };
37661
37662 Roo.tree.TreeSorter.prototype = {
37663     doSort : function(node){
37664         node.sort(this.sortFn);
37665     },
37666     
37667     compareNodes : function(n1, n2){
37668         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37669     },
37670     
37671     updateSort : function(tree, node){
37672         if(node.childrenRendered){
37673             this.doSort.defer(1, this, [node]);
37674         }
37675     }
37676 };/*
37677  * Based on:
37678  * Ext JS Library 1.1.1
37679  * Copyright(c) 2006-2007, Ext JS, LLC.
37680  *
37681  * Originally Released Under LGPL - original licence link has changed is not relivant.
37682  *
37683  * Fork - LGPL
37684  * <script type="text/javascript">
37685  */
37686
37687 if(Roo.dd.DropZone){
37688     
37689 Roo.tree.TreeDropZone = function(tree, config){
37690     this.allowParentInsert = false;
37691     this.allowContainerDrop = false;
37692     this.appendOnly = false;
37693     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
37694     this.tree = tree;
37695     this.lastInsertClass = "x-tree-no-status";
37696     this.dragOverData = {};
37697 };
37698
37699 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
37700     ddGroup : "TreeDD",
37701     scroll:  true,
37702     
37703     expandDelay : 1000,
37704     
37705     expandNode : function(node){
37706         if(node.hasChildNodes() && !node.isExpanded()){
37707             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
37708         }
37709     },
37710     
37711     queueExpand : function(node){
37712         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
37713     },
37714     
37715     cancelExpand : function(){
37716         if(this.expandProcId){
37717             clearTimeout(this.expandProcId);
37718             this.expandProcId = false;
37719         }
37720     },
37721     
37722     isValidDropPoint : function(n, pt, dd, e, data){
37723         if(!n || !data){ return false; }
37724         var targetNode = n.node;
37725         var dropNode = data.node;
37726         // default drop rules
37727         if(!(targetNode && targetNode.isTarget && pt)){
37728             return false;
37729         }
37730         if(pt == "append" && targetNode.allowChildren === false){
37731             return false;
37732         }
37733         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
37734             return false;
37735         }
37736         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
37737             return false;
37738         }
37739         // reuse the object
37740         var overEvent = this.dragOverData;
37741         overEvent.tree = this.tree;
37742         overEvent.target = targetNode;
37743         overEvent.data = data;
37744         overEvent.point = pt;
37745         overEvent.source = dd;
37746         overEvent.rawEvent = e;
37747         overEvent.dropNode = dropNode;
37748         overEvent.cancel = false;  
37749         var result = this.tree.fireEvent("nodedragover", overEvent);
37750         return overEvent.cancel === false && result !== false;
37751     },
37752     
37753     getDropPoint : function(e, n, dd)
37754     {
37755         var tn = n.node;
37756         if(tn.isRoot){
37757             return tn.allowChildren !== false ? "append" : false; // always append for root
37758         }
37759         var dragEl = n.ddel;
37760         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
37761         var y = Roo.lib.Event.getPageY(e);
37762         //var noAppend = tn.allowChildren === false || tn.isLeaf();
37763         
37764         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
37765         var noAppend = tn.allowChildren === false;
37766         if(this.appendOnly || tn.parentNode.allowChildren === false){
37767             return noAppend ? false : "append";
37768         }
37769         var noBelow = false;
37770         if(!this.allowParentInsert){
37771             noBelow = tn.hasChildNodes() && tn.isExpanded();
37772         }
37773         var q = (b - t) / (noAppend ? 2 : 3);
37774         if(y >= t && y < (t + q)){
37775             return "above";
37776         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
37777             return "below";
37778         }else{
37779             return "append";
37780         }
37781     },
37782     
37783     onNodeEnter : function(n, dd, e, data)
37784     {
37785         this.cancelExpand();
37786     },
37787     
37788     onNodeOver : function(n, dd, e, data)
37789     {
37790        
37791         var pt = this.getDropPoint(e, n, dd);
37792         var node = n.node;
37793         
37794         // auto node expand check
37795         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
37796             this.queueExpand(node);
37797         }else if(pt != "append"){
37798             this.cancelExpand();
37799         }
37800         
37801         // set the insert point style on the target node
37802         var returnCls = this.dropNotAllowed;
37803         if(this.isValidDropPoint(n, pt, dd, e, data)){
37804            if(pt){
37805                var el = n.ddel;
37806                var cls;
37807                if(pt == "above"){
37808                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
37809                    cls = "x-tree-drag-insert-above";
37810                }else if(pt == "below"){
37811                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
37812                    cls = "x-tree-drag-insert-below";
37813                }else{
37814                    returnCls = "x-tree-drop-ok-append";
37815                    cls = "x-tree-drag-append";
37816                }
37817                if(this.lastInsertClass != cls){
37818                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
37819                    this.lastInsertClass = cls;
37820                }
37821            }
37822        }
37823        return returnCls;
37824     },
37825     
37826     onNodeOut : function(n, dd, e, data){
37827         
37828         this.cancelExpand();
37829         this.removeDropIndicators(n);
37830     },
37831     
37832     onNodeDrop : function(n, dd, e, data){
37833         var point = this.getDropPoint(e, n, dd);
37834         var targetNode = n.node;
37835         targetNode.ui.startDrop();
37836         if(!this.isValidDropPoint(n, point, dd, e, data)){
37837             targetNode.ui.endDrop();
37838             return false;
37839         }
37840         // first try to find the drop node
37841         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
37842         var dropEvent = {
37843             tree : this.tree,
37844             target: targetNode,
37845             data: data,
37846             point: point,
37847             source: dd,
37848             rawEvent: e,
37849             dropNode: dropNode,
37850             cancel: !dropNode   
37851         };
37852         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
37853         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
37854             targetNode.ui.endDrop();
37855             return false;
37856         }
37857         // allow target changing
37858         targetNode = dropEvent.target;
37859         if(point == "append" && !targetNode.isExpanded()){
37860             targetNode.expand(false, null, function(){
37861                 this.completeDrop(dropEvent);
37862             }.createDelegate(this));
37863         }else{
37864             this.completeDrop(dropEvent);
37865         }
37866         return true;
37867     },
37868     
37869     completeDrop : function(de){
37870         var ns = de.dropNode, p = de.point, t = de.target;
37871         if(!(ns instanceof Array)){
37872             ns = [ns];
37873         }
37874         var n;
37875         for(var i = 0, len = ns.length; i < len; i++){
37876             n = ns[i];
37877             if(p == "above"){
37878                 t.parentNode.insertBefore(n, t);
37879             }else if(p == "below"){
37880                 t.parentNode.insertBefore(n, t.nextSibling);
37881             }else{
37882                 t.appendChild(n);
37883             }
37884         }
37885         n.ui.focus();
37886         if(this.tree.hlDrop){
37887             n.ui.highlight();
37888         }
37889         t.ui.endDrop();
37890         this.tree.fireEvent("nodedrop", de);
37891     },
37892     
37893     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
37894         if(this.tree.hlDrop){
37895             dropNode.ui.focus();
37896             dropNode.ui.highlight();
37897         }
37898         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
37899     },
37900     
37901     getTree : function(){
37902         return this.tree;
37903     },
37904     
37905     removeDropIndicators : function(n){
37906         if(n && n.ddel){
37907             var el = n.ddel;
37908             Roo.fly(el).removeClass([
37909                     "x-tree-drag-insert-above",
37910                     "x-tree-drag-insert-below",
37911                     "x-tree-drag-append"]);
37912             this.lastInsertClass = "_noclass";
37913         }
37914     },
37915     
37916     beforeDragDrop : function(target, e, id){
37917         this.cancelExpand();
37918         return true;
37919     },
37920     
37921     afterRepair : function(data){
37922         if(data && Roo.enableFx){
37923             data.node.ui.highlight();
37924         }
37925         this.hideProxy();
37926     } 
37927     
37928 });
37929
37930 }
37931 /*
37932  * Based on:
37933  * Ext JS Library 1.1.1
37934  * Copyright(c) 2006-2007, Ext JS, LLC.
37935  *
37936  * Originally Released Under LGPL - original licence link has changed is not relivant.
37937  *
37938  * Fork - LGPL
37939  * <script type="text/javascript">
37940  */
37941  
37942
37943 if(Roo.dd.DragZone){
37944 Roo.tree.TreeDragZone = function(tree, config){
37945     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37946     this.tree = tree;
37947 };
37948
37949 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37950     ddGroup : "TreeDD",
37951    
37952     onBeforeDrag : function(data, e){
37953         var n = data.node;
37954         return n && n.draggable && !n.disabled;
37955     },
37956      
37957     
37958     onInitDrag : function(e){
37959         var data = this.dragData;
37960         this.tree.getSelectionModel().select(data.node);
37961         this.proxy.update("");
37962         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37963         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37964     },
37965     
37966     getRepairXY : function(e, data){
37967         return data.node.ui.getDDRepairXY();
37968     },
37969     
37970     onEndDrag : function(data, e){
37971         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37972         
37973         
37974     },
37975     
37976     onValidDrop : function(dd, e, id){
37977         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37978         this.hideProxy();
37979     },
37980     
37981     beforeInvalidDrop : function(e, id){
37982         // this scrolls the original position back into view
37983         var sm = this.tree.getSelectionModel();
37984         sm.clearSelections();
37985         sm.select(this.dragData.node);
37986     }
37987 });
37988 }/*
37989  * Based on:
37990  * Ext JS Library 1.1.1
37991  * Copyright(c) 2006-2007, Ext JS, LLC.
37992  *
37993  * Originally Released Under LGPL - original licence link has changed is not relivant.
37994  *
37995  * Fork - LGPL
37996  * <script type="text/javascript">
37997  */
37998 /**
37999  * @class Roo.tree.TreeEditor
38000  * @extends Roo.Editor
38001  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
38002  * as the editor field.
38003  * @constructor
38004  * @param {Object} config (used to be the tree panel.)
38005  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
38006  * 
38007  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
38008  * @cfg {Roo.form.TextField} field [required] The field configuration
38009  *
38010  * 
38011  */
38012 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38013     var tree = config;
38014     var field;
38015     if (oldconfig) { // old style..
38016         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38017     } else {
38018         // new style..
38019         tree = config.tree;
38020         config.field = config.field  || {};
38021         config.field.xtype = 'TextField';
38022         field = Roo.factory(config.field, Roo.form);
38023     }
38024     config = config || {};
38025     
38026     
38027     this.addEvents({
38028         /**
38029          * @event beforenodeedit
38030          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
38031          * false from the handler of this event.
38032          * @param {Editor} this
38033          * @param {Roo.tree.Node} node 
38034          */
38035         "beforenodeedit" : true
38036     });
38037     
38038     //Roo.log(config);
38039     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38040
38041     this.tree = tree;
38042
38043     tree.on('beforeclick', this.beforeNodeClick, this);
38044     tree.getTreeEl().on('mousedown', this.hide, this);
38045     this.on('complete', this.updateNode, this);
38046     this.on('beforestartedit', this.fitToTree, this);
38047     this.on('startedit', this.bindScroll, this, {delay:10});
38048     this.on('specialkey', this.onSpecialKey, this);
38049 };
38050
38051 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38052     /**
38053      * @cfg {String} alignment
38054      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38055      */
38056     alignment: "l-l",
38057     // inherit
38058     autoSize: false,
38059     /**
38060      * @cfg {Boolean} hideEl
38061      * True to hide the bound element while the editor is displayed (defaults to false)
38062      */
38063     hideEl : false,
38064     /**
38065      * @cfg {String} cls
38066      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38067      */
38068     cls: "x-small-editor x-tree-editor",
38069     /**
38070      * @cfg {Boolean} shim
38071      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38072      */
38073     shim:false,
38074     // inherit
38075     shadow:"frame",
38076     /**
38077      * @cfg {Number} maxWidth
38078      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38079      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38080      * scroll and client offsets into account prior to each edit.
38081      */
38082     maxWidth: 250,
38083
38084     editDelay : 350,
38085
38086     // private
38087     fitToTree : function(ed, el){
38088         var td = this.tree.getTreeEl().dom, nd = el.dom;
38089         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38090             td.scrollLeft = nd.offsetLeft;
38091         }
38092         var w = Math.min(
38093                 this.maxWidth,
38094                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38095         this.setSize(w, '');
38096         
38097         return this.fireEvent('beforenodeedit', this, this.editNode);
38098         
38099     },
38100
38101     // private
38102     triggerEdit : function(node){
38103         this.completeEdit();
38104         this.editNode = node;
38105         this.startEdit(node.ui.textNode, node.text);
38106     },
38107
38108     // private
38109     bindScroll : function(){
38110         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38111     },
38112
38113     // private
38114     beforeNodeClick : function(node, e){
38115         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38116         this.lastClick = new Date();
38117         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38118             e.stopEvent();
38119             this.triggerEdit(node);
38120             return false;
38121         }
38122         return true;
38123     },
38124
38125     // private
38126     updateNode : function(ed, value){
38127         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38128         this.editNode.setText(value);
38129     },
38130
38131     // private
38132     onHide : function(){
38133         Roo.tree.TreeEditor.superclass.onHide.call(this);
38134         if(this.editNode){
38135             this.editNode.ui.focus();
38136         }
38137     },
38138
38139     // private
38140     onSpecialKey : function(field, e){
38141         var k = e.getKey();
38142         if(k == e.ESC){
38143             e.stopEvent();
38144             this.cancelEdit();
38145         }else if(k == e.ENTER && !e.hasModifier()){
38146             e.stopEvent();
38147             this.completeEdit();
38148         }
38149     }
38150 });//<Script type="text/javascript">
38151 /*
38152  * Based on:
38153  * Ext JS Library 1.1.1
38154  * Copyright(c) 2006-2007, Ext JS, LLC.
38155  *
38156  * Originally Released Under LGPL - original licence link has changed is not relivant.
38157  *
38158  * Fork - LGPL
38159  * <script type="text/javascript">
38160  */
38161  
38162 /**
38163  * Not documented??? - probably should be...
38164  */
38165
38166 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38167     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38168     
38169     renderElements : function(n, a, targetNode, bulkRender){
38170         //consel.log("renderElements?");
38171         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38172
38173         var t = n.getOwnerTree();
38174         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38175         
38176         var cols = t.columns;
38177         var bw = t.borderWidth;
38178         var c = cols[0];
38179         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38180          var cb = typeof a.checked == "boolean";
38181         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38182         var colcls = 'x-t-' + tid + '-c0';
38183         var buf = [
38184             '<li class="x-tree-node">',
38185             
38186                 
38187                 '<div class="x-tree-node-el ', a.cls,'">',
38188                     // extran...
38189                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38190                 
38191                 
38192                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38193                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38194                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38195                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38196                            (a.iconCls ? ' '+a.iconCls : ''),
38197                            '" unselectable="on" />',
38198                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38199                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38200                              
38201                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38202                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38203                             '<span unselectable="on" qtip="' + tx + '">',
38204                              tx,
38205                              '</span></a>' ,
38206                     '</div>',
38207                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38208                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38209                  ];
38210         for(var i = 1, len = cols.length; i < len; i++){
38211             c = cols[i];
38212             colcls = 'x-t-' + tid + '-c' +i;
38213             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38214             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38215                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38216                       "</div>");
38217          }
38218          
38219          buf.push(
38220             '</a>',
38221             '<div class="x-clear"></div></div>',
38222             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38223             "</li>");
38224         
38225         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38226             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38227                                 n.nextSibling.ui.getEl(), buf.join(""));
38228         }else{
38229             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38230         }
38231         var el = this.wrap.firstChild;
38232         this.elRow = el;
38233         this.elNode = el.firstChild;
38234         this.ranchor = el.childNodes[1];
38235         this.ctNode = this.wrap.childNodes[1];
38236         var cs = el.firstChild.childNodes;
38237         this.indentNode = cs[0];
38238         this.ecNode = cs[1];
38239         this.iconNode = cs[2];
38240         var index = 3;
38241         if(cb){
38242             this.checkbox = cs[3];
38243             index++;
38244         }
38245         this.anchor = cs[index];
38246         
38247         this.textNode = cs[index].firstChild;
38248         
38249         //el.on("click", this.onClick, this);
38250         //el.on("dblclick", this.onDblClick, this);
38251         
38252         
38253        // console.log(this);
38254     },
38255     initEvents : function(){
38256         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38257         
38258             
38259         var a = this.ranchor;
38260
38261         var el = Roo.get(a);
38262
38263         if(Roo.isOpera){ // opera render bug ignores the CSS
38264             el.setStyle("text-decoration", "none");
38265         }
38266
38267         el.on("click", this.onClick, this);
38268         el.on("dblclick", this.onDblClick, this);
38269         el.on("contextmenu", this.onContextMenu, this);
38270         
38271     },
38272     
38273     /*onSelectedChange : function(state){
38274         if(state){
38275             this.focus();
38276             this.addClass("x-tree-selected");
38277         }else{
38278             //this.blur();
38279             this.removeClass("x-tree-selected");
38280         }
38281     },*/
38282     addClass : function(cls){
38283         if(this.elRow){
38284             Roo.fly(this.elRow).addClass(cls);
38285         }
38286         
38287     },
38288     
38289     
38290     removeClass : function(cls){
38291         if(this.elRow){
38292             Roo.fly(this.elRow).removeClass(cls);
38293         }
38294     }
38295
38296     
38297     
38298 });//<Script type="text/javascript">
38299
38300 /*
38301  * Based on:
38302  * Ext JS Library 1.1.1
38303  * Copyright(c) 2006-2007, Ext JS, LLC.
38304  *
38305  * Originally Released Under LGPL - original licence link has changed is not relivant.
38306  *
38307  * Fork - LGPL
38308  * <script type="text/javascript">
38309  */
38310  
38311
38312 /**
38313  * @class Roo.tree.ColumnTree
38314  * @extends Roo.tree.TreePanel
38315  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38316  * @cfg {int} borderWidth  compined right/left border allowance
38317  * @constructor
38318  * @param {String/HTMLElement/Element} el The container element
38319  * @param {Object} config
38320  */
38321 Roo.tree.ColumnTree =  function(el, config)
38322 {
38323    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38324    this.addEvents({
38325         /**
38326         * @event resize
38327         * Fire this event on a container when it resizes
38328         * @param {int} w Width
38329         * @param {int} h Height
38330         */
38331        "resize" : true
38332     });
38333     this.on('resize', this.onResize, this);
38334 };
38335
38336 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38337     //lines:false,
38338     
38339     
38340     borderWidth: Roo.isBorderBox ? 0 : 2, 
38341     headEls : false,
38342     
38343     render : function(){
38344         // add the header.....
38345        
38346         Roo.tree.ColumnTree.superclass.render.apply(this);
38347         
38348         this.el.addClass('x-column-tree');
38349         
38350         this.headers = this.el.createChild(
38351             {cls:'x-tree-headers'},this.innerCt.dom);
38352    
38353         var cols = this.columns, c;
38354         var totalWidth = 0;
38355         this.headEls = [];
38356         var  len = cols.length;
38357         for(var i = 0; i < len; i++){
38358              c = cols[i];
38359              totalWidth += c.width;
38360             this.headEls.push(this.headers.createChild({
38361                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38362                  cn: {
38363                      cls:'x-tree-hd-text',
38364                      html: c.header
38365                  },
38366                  style:'width:'+(c.width-this.borderWidth)+'px;'
38367              }));
38368         }
38369         this.headers.createChild({cls:'x-clear'});
38370         // prevent floats from wrapping when clipped
38371         this.headers.setWidth(totalWidth);
38372         //this.innerCt.setWidth(totalWidth);
38373         this.innerCt.setStyle({ overflow: 'auto' });
38374         this.onResize(this.width, this.height);
38375              
38376         
38377     },
38378     onResize : function(w,h)
38379     {
38380         this.height = h;
38381         this.width = w;
38382         // resize cols..
38383         this.innerCt.setWidth(this.width);
38384         this.innerCt.setHeight(this.height-20);
38385         
38386         // headers...
38387         var cols = this.columns, c;
38388         var totalWidth = 0;
38389         var expEl = false;
38390         var len = cols.length;
38391         for(var i = 0; i < len; i++){
38392             c = cols[i];
38393             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38394                 // it's the expander..
38395                 expEl  = this.headEls[i];
38396                 continue;
38397             }
38398             totalWidth += c.width;
38399             
38400         }
38401         if (expEl) {
38402             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38403         }
38404         this.headers.setWidth(w-20);
38405
38406         
38407         
38408         
38409     }
38410 });
38411 /*
38412  * Based on:
38413  * Ext JS Library 1.1.1
38414  * Copyright(c) 2006-2007, Ext JS, LLC.
38415  *
38416  * Originally Released Under LGPL - original licence link has changed is not relivant.
38417  *
38418  * Fork - LGPL
38419  * <script type="text/javascript">
38420  */
38421  
38422 /**
38423  * @class Roo.menu.Menu
38424  * @extends Roo.util.Observable
38425  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38426  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38427  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38428  * @constructor
38429  * Creates a new Menu
38430  * @param {Object} config Configuration options
38431  */
38432 Roo.menu.Menu = function(config){
38433     
38434     Roo.menu.Menu.superclass.constructor.call(this, config);
38435     
38436     this.id = this.id || Roo.id();
38437     this.addEvents({
38438         /**
38439          * @event beforeshow
38440          * Fires before this menu is displayed
38441          * @param {Roo.menu.Menu} this
38442          */
38443         beforeshow : true,
38444         /**
38445          * @event beforehide
38446          * Fires before this menu is hidden
38447          * @param {Roo.menu.Menu} this
38448          */
38449         beforehide : true,
38450         /**
38451          * @event show
38452          * Fires after this menu is displayed
38453          * @param {Roo.menu.Menu} this
38454          */
38455         show : true,
38456         /**
38457          * @event hide
38458          * Fires after this menu is hidden
38459          * @param {Roo.menu.Menu} this
38460          */
38461         hide : true,
38462         /**
38463          * @event click
38464          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38465          * @param {Roo.menu.Menu} this
38466          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38467          * @param {Roo.EventObject} e
38468          */
38469         click : true,
38470         /**
38471          * @event mouseover
38472          * Fires when the mouse is hovering over this menu
38473          * @param {Roo.menu.Menu} this
38474          * @param {Roo.EventObject} e
38475          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38476          */
38477         mouseover : true,
38478         /**
38479          * @event mouseout
38480          * Fires when the mouse exits this menu
38481          * @param {Roo.menu.Menu} this
38482          * @param {Roo.EventObject} e
38483          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38484          */
38485         mouseout : true,
38486         /**
38487          * @event itemclick
38488          * Fires when a menu item contained in this menu is clicked
38489          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38490          * @param {Roo.EventObject} e
38491          */
38492         itemclick: true
38493     });
38494     if (this.registerMenu) {
38495         Roo.menu.MenuMgr.register(this);
38496     }
38497     
38498     var mis = this.items;
38499     this.items = new Roo.util.MixedCollection();
38500     if(mis){
38501         this.add.apply(this, mis);
38502     }
38503 };
38504
38505 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38506     /**
38507      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38508      */
38509     minWidth : 120,
38510     /**
38511      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38512      * for bottom-right shadow (defaults to "sides")
38513      */
38514     shadow : "sides",
38515     /**
38516      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38517      * this menu (defaults to "tl-tr?")
38518      */
38519     subMenuAlign : "tl-tr?",
38520     /**
38521      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38522      * relative to its element of origin (defaults to "tl-bl?")
38523      */
38524     defaultAlign : "tl-bl?",
38525     /**
38526      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38527      */
38528     allowOtherMenus : false,
38529     /**
38530      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38531      */
38532     registerMenu : true,
38533
38534     hidden:true,
38535
38536     // private
38537     render : function(){
38538         if(this.el){
38539             return;
38540         }
38541         var el = this.el = new Roo.Layer({
38542             cls: "x-menu",
38543             shadow:this.shadow,
38544             constrain: false,
38545             parentEl: this.parentEl || document.body,
38546             zindex:15000
38547         });
38548
38549         this.keyNav = new Roo.menu.MenuNav(this);
38550
38551         if(this.plain){
38552             el.addClass("x-menu-plain");
38553         }
38554         if(this.cls){
38555             el.addClass(this.cls);
38556         }
38557         // generic focus element
38558         this.focusEl = el.createChild({
38559             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38560         });
38561         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38562         //disabling touch- as it's causing issues ..
38563         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38564         ul.on('click'   , this.onClick, this);
38565         
38566         
38567         ul.on("mouseover", this.onMouseOver, this);
38568         ul.on("mouseout", this.onMouseOut, this);
38569         this.items.each(function(item){
38570             if (item.hidden) {
38571                 return;
38572             }
38573             
38574             var li = document.createElement("li");
38575             li.className = "x-menu-list-item";
38576             ul.dom.appendChild(li);
38577             item.render(li, this);
38578         }, this);
38579         this.ul = ul;
38580         this.autoWidth();
38581     },
38582
38583     // private
38584     autoWidth : function(){
38585         var el = this.el, ul = this.ul;
38586         if(!el){
38587             return;
38588         }
38589         var w = this.width;
38590         if(w){
38591             el.setWidth(w);
38592         }else if(Roo.isIE){
38593             el.setWidth(this.minWidth);
38594             var t = el.dom.offsetWidth; // force recalc
38595             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38596         }
38597     },
38598
38599     // private
38600     delayAutoWidth : function(){
38601         if(this.rendered){
38602             if(!this.awTask){
38603                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38604             }
38605             this.awTask.delay(20);
38606         }
38607     },
38608
38609     // private
38610     findTargetItem : function(e){
38611         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38612         if(t && t.menuItemId){
38613             return this.items.get(t.menuItemId);
38614         }
38615     },
38616
38617     // private
38618     onClick : function(e){
38619         Roo.log("menu.onClick");
38620         var t = this.findTargetItem(e);
38621         if(!t){
38622             return;
38623         }
38624         Roo.log(e);
38625         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38626             if(t == this.activeItem && t.shouldDeactivate(e)){
38627                 this.activeItem.deactivate();
38628                 delete this.activeItem;
38629                 return;
38630             }
38631             if(t.canActivate){
38632                 this.setActiveItem(t, true);
38633             }
38634             return;
38635             
38636             
38637         }
38638         
38639         t.onClick(e);
38640         this.fireEvent("click", this, t, e);
38641     },
38642
38643     // private
38644     setActiveItem : function(item, autoExpand){
38645         if(item != this.activeItem){
38646             if(this.activeItem){
38647                 this.activeItem.deactivate();
38648             }
38649             this.activeItem = item;
38650             item.activate(autoExpand);
38651         }else if(autoExpand){
38652             item.expandMenu();
38653         }
38654     },
38655
38656     // private
38657     tryActivate : function(start, step){
38658         var items = this.items;
38659         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38660             var item = items.get(i);
38661             if(!item.disabled && item.canActivate){
38662                 this.setActiveItem(item, false);
38663                 return item;
38664             }
38665         }
38666         return false;
38667     },
38668
38669     // private
38670     onMouseOver : function(e){
38671         var t;
38672         if(t = this.findTargetItem(e)){
38673             if(t.canActivate && !t.disabled){
38674                 this.setActiveItem(t, true);
38675             }
38676         }
38677         this.fireEvent("mouseover", this, e, t);
38678     },
38679
38680     // private
38681     onMouseOut : function(e){
38682         var t;
38683         if(t = this.findTargetItem(e)){
38684             if(t == this.activeItem && t.shouldDeactivate(e)){
38685                 this.activeItem.deactivate();
38686                 delete this.activeItem;
38687             }
38688         }
38689         this.fireEvent("mouseout", this, e, t);
38690     },
38691
38692     /**
38693      * Read-only.  Returns true if the menu is currently displayed, else false.
38694      * @type Boolean
38695      */
38696     isVisible : function(){
38697         return this.el && !this.hidden;
38698     },
38699
38700     /**
38701      * Displays this menu relative to another element
38702      * @param {String/HTMLElement/Roo.Element} element The element to align to
38703      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
38704      * the element (defaults to this.defaultAlign)
38705      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38706      */
38707     show : function(el, pos, parentMenu){
38708         this.parentMenu = parentMenu;
38709         if(!this.el){
38710             this.render();
38711         }
38712         this.fireEvent("beforeshow", this);
38713         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
38714     },
38715
38716     /**
38717      * Displays this menu at a specific xy position
38718      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
38719      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38720      */
38721     showAt : function(xy, parentMenu, /* private: */_e){
38722         this.parentMenu = parentMenu;
38723         if(!this.el){
38724             this.render();
38725         }
38726         if(_e !== false){
38727             this.fireEvent("beforeshow", this);
38728             xy = this.el.adjustForConstraints(xy);
38729         }
38730         this.el.setXY(xy);
38731         this.el.show();
38732         this.hidden = false;
38733         this.focus();
38734         this.fireEvent("show", this);
38735     },
38736
38737     focus : function(){
38738         if(!this.hidden){
38739             this.doFocus.defer(50, this);
38740         }
38741     },
38742
38743     doFocus : function(){
38744         if(!this.hidden){
38745             this.focusEl.focus();
38746         }
38747     },
38748
38749     /**
38750      * Hides this menu and optionally all parent menus
38751      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
38752      */
38753     hide : function(deep){
38754         if(this.el && this.isVisible()){
38755             this.fireEvent("beforehide", this);
38756             if(this.activeItem){
38757                 this.activeItem.deactivate();
38758                 this.activeItem = null;
38759             }
38760             this.el.hide();
38761             this.hidden = true;
38762             this.fireEvent("hide", this);
38763         }
38764         if(deep === true && this.parentMenu){
38765             this.parentMenu.hide(true);
38766         }
38767     },
38768
38769     /**
38770      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
38771      * Any of the following are valid:
38772      * <ul>
38773      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
38774      * <li>An HTMLElement object which will be converted to a menu item</li>
38775      * <li>A menu item config object that will be created as a new menu item</li>
38776      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
38777      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
38778      * </ul>
38779      * Usage:
38780      * <pre><code>
38781 // Create the menu
38782 var menu = new Roo.menu.Menu();
38783
38784 // Create a menu item to add by reference
38785 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
38786
38787 // Add a bunch of items at once using different methods.
38788 // Only the last item added will be returned.
38789 var item = menu.add(
38790     menuItem,                // add existing item by ref
38791     'Dynamic Item',          // new TextItem
38792     '-',                     // new separator
38793     { text: 'Config Item' }  // new item by config
38794 );
38795 </code></pre>
38796      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
38797      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
38798      */
38799     add : function(){
38800         var a = arguments, l = a.length, item;
38801         for(var i = 0; i < l; i++){
38802             var el = a[i];
38803             if ((typeof(el) == "object") && el.xtype && el.xns) {
38804                 el = Roo.factory(el, Roo.menu);
38805             }
38806             
38807             if(el.render){ // some kind of Item
38808                 item = this.addItem(el);
38809             }else if(typeof el == "string"){ // string
38810                 if(el == "separator" || el == "-"){
38811                     item = this.addSeparator();
38812                 }else{
38813                     item = this.addText(el);
38814                 }
38815             }else if(el.tagName || el.el){ // element
38816                 item = this.addElement(el);
38817             }else if(typeof el == "object"){ // must be menu item config?
38818                 item = this.addMenuItem(el);
38819             }
38820         }
38821         return item;
38822     },
38823
38824     /**
38825      * Returns this menu's underlying {@link Roo.Element} object
38826      * @return {Roo.Element} The element
38827      */
38828     getEl : function(){
38829         if(!this.el){
38830             this.render();
38831         }
38832         return this.el;
38833     },
38834
38835     /**
38836      * Adds a separator bar to the menu
38837      * @return {Roo.menu.Item} The menu item that was added
38838      */
38839     addSeparator : function(){
38840         return this.addItem(new Roo.menu.Separator());
38841     },
38842
38843     /**
38844      * Adds an {@link Roo.Element} object to the menu
38845      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
38846      * @return {Roo.menu.Item} The menu item that was added
38847      */
38848     addElement : function(el){
38849         return this.addItem(new Roo.menu.BaseItem(el));
38850     },
38851
38852     /**
38853      * Adds an existing object based on {@link Roo.menu.Item} to the menu
38854      * @param {Roo.menu.Item} item The menu item to add
38855      * @return {Roo.menu.Item} The menu item that was added
38856      */
38857     addItem : function(item){
38858         this.items.add(item);
38859         if(this.ul){
38860             var li = document.createElement("li");
38861             li.className = "x-menu-list-item";
38862             this.ul.dom.appendChild(li);
38863             item.render(li, this);
38864             this.delayAutoWidth();
38865         }
38866         return item;
38867     },
38868
38869     /**
38870      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
38871      * @param {Object} config A MenuItem config object
38872      * @return {Roo.menu.Item} The menu item that was added
38873      */
38874     addMenuItem : function(config){
38875         if(!(config instanceof Roo.menu.Item)){
38876             if(typeof config.checked == "boolean"){ // must be check menu item config?
38877                 config = new Roo.menu.CheckItem(config);
38878             }else{
38879                 config = new Roo.menu.Item(config);
38880             }
38881         }
38882         return this.addItem(config);
38883     },
38884
38885     /**
38886      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
38887      * @param {String} text The text to display in the menu item
38888      * @return {Roo.menu.Item} The menu item that was added
38889      */
38890     addText : function(text){
38891         return this.addItem(new Roo.menu.TextItem({ text : text }));
38892     },
38893
38894     /**
38895      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
38896      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
38897      * @param {Roo.menu.Item} item The menu item to add
38898      * @return {Roo.menu.Item} The menu item that was added
38899      */
38900     insert : function(index, item){
38901         this.items.insert(index, item);
38902         if(this.ul){
38903             var li = document.createElement("li");
38904             li.className = "x-menu-list-item";
38905             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
38906             item.render(li, this);
38907             this.delayAutoWidth();
38908         }
38909         return item;
38910     },
38911
38912     /**
38913      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
38914      * @param {Roo.menu.Item} item The menu item to remove
38915      */
38916     remove : function(item){
38917         this.items.removeKey(item.id);
38918         item.destroy();
38919     },
38920
38921     /**
38922      * Removes and destroys all items in the menu
38923      */
38924     removeAll : function(){
38925         var f;
38926         while(f = this.items.first()){
38927             this.remove(f);
38928         }
38929     }
38930 });
38931
38932 // MenuNav is a private utility class used internally by the Menu
38933 Roo.menu.MenuNav = function(menu){
38934     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38935     this.scope = this.menu = menu;
38936 };
38937
38938 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38939     doRelay : function(e, h){
38940         var k = e.getKey();
38941         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38942             this.menu.tryActivate(0, 1);
38943             return false;
38944         }
38945         return h.call(this.scope || this, e, this.menu);
38946     },
38947
38948     up : function(e, m){
38949         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38950             m.tryActivate(m.items.length-1, -1);
38951         }
38952     },
38953
38954     down : function(e, m){
38955         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38956             m.tryActivate(0, 1);
38957         }
38958     },
38959
38960     right : function(e, m){
38961         if(m.activeItem){
38962             m.activeItem.expandMenu(true);
38963         }
38964     },
38965
38966     left : function(e, m){
38967         m.hide();
38968         if(m.parentMenu && m.parentMenu.activeItem){
38969             m.parentMenu.activeItem.activate();
38970         }
38971     },
38972
38973     enter : function(e, m){
38974         if(m.activeItem){
38975             e.stopPropagation();
38976             m.activeItem.onClick(e);
38977             m.fireEvent("click", this, m.activeItem);
38978             return true;
38979         }
38980     }
38981 });/*
38982  * Based on:
38983  * Ext JS Library 1.1.1
38984  * Copyright(c) 2006-2007, Ext JS, LLC.
38985  *
38986  * Originally Released Under LGPL - original licence link has changed is not relivant.
38987  *
38988  * Fork - LGPL
38989  * <script type="text/javascript">
38990  */
38991  
38992 /**
38993  * @class Roo.menu.MenuMgr
38994  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38995  * @static
38996  */
38997 Roo.menu.MenuMgr = function(){
38998    var menus, active, groups = {}, attached = false, lastShow = new Date();
38999
39000    // private - called when first menu is created
39001    function init(){
39002        menus = {};
39003        active = new Roo.util.MixedCollection();
39004        Roo.get(document).addKeyListener(27, function(){
39005            if(active.length > 0){
39006                hideAll();
39007            }
39008        });
39009    }
39010
39011    // private
39012    function hideAll(){
39013        if(active && active.length > 0){
39014            var c = active.clone();
39015            c.each(function(m){
39016                m.hide();
39017            });
39018        }
39019    }
39020
39021    // private
39022    function onHide(m){
39023        active.remove(m);
39024        if(active.length < 1){
39025            Roo.get(document).un("mousedown", onMouseDown);
39026            attached = false;
39027        }
39028    }
39029
39030    // private
39031    function onShow(m){
39032        var last = active.last();
39033        lastShow = new Date();
39034        active.add(m);
39035        if(!attached){
39036            Roo.get(document).on("mousedown", onMouseDown);
39037            attached = true;
39038        }
39039        if(m.parentMenu){
39040           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39041           m.parentMenu.activeChild = m;
39042        }else if(last && last.isVisible()){
39043           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39044        }
39045    }
39046
39047    // private
39048    function onBeforeHide(m){
39049        if(m.activeChild){
39050            m.activeChild.hide();
39051        }
39052        if(m.autoHideTimer){
39053            clearTimeout(m.autoHideTimer);
39054            delete m.autoHideTimer;
39055        }
39056    }
39057
39058    // private
39059    function onBeforeShow(m){
39060        var pm = m.parentMenu;
39061        if(!pm && !m.allowOtherMenus){
39062            hideAll();
39063        }else if(pm && pm.activeChild && active != m){
39064            pm.activeChild.hide();
39065        }
39066    }
39067
39068    // private
39069    function onMouseDown(e){
39070        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39071            hideAll();
39072        }
39073    }
39074
39075    // private
39076    function onBeforeCheck(mi, state){
39077        if(state){
39078            var g = groups[mi.group];
39079            for(var i = 0, l = g.length; i < l; i++){
39080                if(g[i] != mi){
39081                    g[i].setChecked(false);
39082                }
39083            }
39084        }
39085    }
39086
39087    return {
39088
39089        /**
39090         * Hides all menus that are currently visible
39091         */
39092        hideAll : function(){
39093             hideAll();  
39094        },
39095
39096        // private
39097        register : function(menu){
39098            if(!menus){
39099                init();
39100            }
39101            menus[menu.id] = menu;
39102            menu.on("beforehide", onBeforeHide);
39103            menu.on("hide", onHide);
39104            menu.on("beforeshow", onBeforeShow);
39105            menu.on("show", onShow);
39106            var g = menu.group;
39107            if(g && menu.events["checkchange"]){
39108                if(!groups[g]){
39109                    groups[g] = [];
39110                }
39111                groups[g].push(menu);
39112                menu.on("checkchange", onCheck);
39113            }
39114        },
39115
39116         /**
39117          * Returns a {@link Roo.menu.Menu} object
39118          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39119          * be used to generate and return a new Menu instance.
39120          */
39121        get : function(menu){
39122            if(typeof menu == "string"){ // menu id
39123                return menus[menu];
39124            }else if(menu.events){  // menu instance
39125                return menu;
39126            }else if(typeof menu.length == 'number'){ // array of menu items?
39127                return new Roo.menu.Menu({items:menu});
39128            }else{ // otherwise, must be a config
39129                return new Roo.menu.Menu(menu);
39130            }
39131        },
39132
39133        // private
39134        unregister : function(menu){
39135            delete menus[menu.id];
39136            menu.un("beforehide", onBeforeHide);
39137            menu.un("hide", onHide);
39138            menu.un("beforeshow", onBeforeShow);
39139            menu.un("show", onShow);
39140            var g = menu.group;
39141            if(g && menu.events["checkchange"]){
39142                groups[g].remove(menu);
39143                menu.un("checkchange", onCheck);
39144            }
39145        },
39146
39147        // private
39148        registerCheckable : function(menuItem){
39149            var g = menuItem.group;
39150            if(g){
39151                if(!groups[g]){
39152                    groups[g] = [];
39153                }
39154                groups[g].push(menuItem);
39155                menuItem.on("beforecheckchange", onBeforeCheck);
39156            }
39157        },
39158
39159        // private
39160        unregisterCheckable : function(menuItem){
39161            var g = menuItem.group;
39162            if(g){
39163                groups[g].remove(menuItem);
39164                menuItem.un("beforecheckchange", onBeforeCheck);
39165            }
39166        }
39167    };
39168 }();/*
39169  * Based on:
39170  * Ext JS Library 1.1.1
39171  * Copyright(c) 2006-2007, Ext JS, LLC.
39172  *
39173  * Originally Released Under LGPL - original licence link has changed is not relivant.
39174  *
39175  * Fork - LGPL
39176  * <script type="text/javascript">
39177  */
39178  
39179
39180 /**
39181  * @class Roo.menu.BaseItem
39182  * @extends Roo.Component
39183  * @abstract
39184  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39185  * management and base configuration options shared by all menu components.
39186  * @constructor
39187  * Creates a new BaseItem
39188  * @param {Object} config Configuration options
39189  */
39190 Roo.menu.BaseItem = function(config){
39191     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39192
39193     this.addEvents({
39194         /**
39195          * @event click
39196          * Fires when this item is clicked
39197          * @param {Roo.menu.BaseItem} this
39198          * @param {Roo.EventObject} e
39199          */
39200         click: true,
39201         /**
39202          * @event activate
39203          * Fires when this item is activated
39204          * @param {Roo.menu.BaseItem} this
39205          */
39206         activate : true,
39207         /**
39208          * @event deactivate
39209          * Fires when this item is deactivated
39210          * @param {Roo.menu.BaseItem} this
39211          */
39212         deactivate : true
39213     });
39214
39215     if(this.handler){
39216         this.on("click", this.handler, this.scope, true);
39217     }
39218 };
39219
39220 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39221     /**
39222      * @cfg {Function} handler
39223      * A function that will handle the click event of this menu item (defaults to undefined)
39224      */
39225     /**
39226      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39227      */
39228     canActivate : false,
39229     
39230      /**
39231      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39232      */
39233     hidden: false,
39234     
39235     /**
39236      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39237      */
39238     activeClass : "x-menu-item-active",
39239     /**
39240      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39241      */
39242     hideOnClick : true,
39243     /**
39244      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39245      */
39246     hideDelay : 100,
39247
39248     // private
39249     ctype: "Roo.menu.BaseItem",
39250
39251     // private
39252     actionMode : "container",
39253
39254     // private
39255     render : function(container, parentMenu){
39256         this.parentMenu = parentMenu;
39257         Roo.menu.BaseItem.superclass.render.call(this, container);
39258         this.container.menuItemId = this.id;
39259     },
39260
39261     // private
39262     onRender : function(container, position){
39263         this.el = Roo.get(this.el);
39264         container.dom.appendChild(this.el.dom);
39265     },
39266
39267     // private
39268     onClick : function(e){
39269         if(!this.disabled && this.fireEvent("click", this, e) !== false
39270                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39271             this.handleClick(e);
39272         }else{
39273             e.stopEvent();
39274         }
39275     },
39276
39277     // private
39278     activate : function(){
39279         if(this.disabled){
39280             return false;
39281         }
39282         var li = this.container;
39283         li.addClass(this.activeClass);
39284         this.region = li.getRegion().adjust(2, 2, -2, -2);
39285         this.fireEvent("activate", this);
39286         return true;
39287     },
39288
39289     // private
39290     deactivate : function(){
39291         this.container.removeClass(this.activeClass);
39292         this.fireEvent("deactivate", this);
39293     },
39294
39295     // private
39296     shouldDeactivate : function(e){
39297         return !this.region || !this.region.contains(e.getPoint());
39298     },
39299
39300     // private
39301     handleClick : function(e){
39302         if(this.hideOnClick){
39303             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39304         }
39305     },
39306
39307     // private
39308     expandMenu : function(autoActivate){
39309         // do nothing
39310     },
39311
39312     // private
39313     hideMenu : function(){
39314         // do nothing
39315     }
39316 });/*
39317  * Based on:
39318  * Ext JS Library 1.1.1
39319  * Copyright(c) 2006-2007, Ext JS, LLC.
39320  *
39321  * Originally Released Under LGPL - original licence link has changed is not relivant.
39322  *
39323  * Fork - LGPL
39324  * <script type="text/javascript">
39325  */
39326  
39327 /**
39328  * @class Roo.menu.Adapter
39329  * @extends Roo.menu.BaseItem
39330  * @abstract
39331  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
39332  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39333  * @constructor
39334  * Creates a new Adapter
39335  * @param {Object} config Configuration options
39336  */
39337 Roo.menu.Adapter = function(component, config){
39338     Roo.menu.Adapter.superclass.constructor.call(this, config);
39339     this.component = component;
39340 };
39341 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39342     // private
39343     canActivate : true,
39344
39345     // private
39346     onRender : function(container, position){
39347         this.component.render(container);
39348         this.el = this.component.getEl();
39349     },
39350
39351     // private
39352     activate : function(){
39353         if(this.disabled){
39354             return false;
39355         }
39356         this.component.focus();
39357         this.fireEvent("activate", this);
39358         return true;
39359     },
39360
39361     // private
39362     deactivate : function(){
39363         this.fireEvent("deactivate", this);
39364     },
39365
39366     // private
39367     disable : function(){
39368         this.component.disable();
39369         Roo.menu.Adapter.superclass.disable.call(this);
39370     },
39371
39372     // private
39373     enable : function(){
39374         this.component.enable();
39375         Roo.menu.Adapter.superclass.enable.call(this);
39376     }
39377 });/*
39378  * Based on:
39379  * Ext JS Library 1.1.1
39380  * Copyright(c) 2006-2007, Ext JS, LLC.
39381  *
39382  * Originally Released Under LGPL - original licence link has changed is not relivant.
39383  *
39384  * Fork - LGPL
39385  * <script type="text/javascript">
39386  */
39387
39388 /**
39389  * @class Roo.menu.TextItem
39390  * @extends Roo.menu.BaseItem
39391  * Adds a static text string to a menu, usually used as either a heading or group separator.
39392  * Note: old style constructor with text is still supported.
39393  * 
39394  * @constructor
39395  * Creates a new TextItem
39396  * @param {Object} cfg Configuration
39397  */
39398 Roo.menu.TextItem = function(cfg){
39399     if (typeof(cfg) == 'string') {
39400         this.text = cfg;
39401     } else {
39402         Roo.apply(this,cfg);
39403     }
39404     
39405     Roo.menu.TextItem.superclass.constructor.call(this);
39406 };
39407
39408 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39409     /**
39410      * @cfg {String} text Text to show on item.
39411      */
39412     text : '',
39413     
39414     /**
39415      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39416      */
39417     hideOnClick : false,
39418     /**
39419      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39420      */
39421     itemCls : "x-menu-text",
39422
39423     // private
39424     onRender : function(){
39425         var s = document.createElement("span");
39426         s.className = this.itemCls;
39427         s.innerHTML = this.text;
39428         this.el = s;
39429         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39430     }
39431 });/*
39432  * Based on:
39433  * Ext JS Library 1.1.1
39434  * Copyright(c) 2006-2007, Ext JS, LLC.
39435  *
39436  * Originally Released Under LGPL - original licence link has changed is not relivant.
39437  *
39438  * Fork - LGPL
39439  * <script type="text/javascript">
39440  */
39441
39442 /**
39443  * @class Roo.menu.Separator
39444  * @extends Roo.menu.BaseItem
39445  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39446  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39447  * @constructor
39448  * @param {Object} config Configuration options
39449  */
39450 Roo.menu.Separator = function(config){
39451     Roo.menu.Separator.superclass.constructor.call(this, config);
39452 };
39453
39454 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39455     /**
39456      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39457      */
39458     itemCls : "x-menu-sep",
39459     /**
39460      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39461      */
39462     hideOnClick : false,
39463
39464     // private
39465     onRender : function(li){
39466         var s = document.createElement("span");
39467         s.className = this.itemCls;
39468         s.innerHTML = "&#160;";
39469         this.el = s;
39470         li.addClass("x-menu-sep-li");
39471         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39472     }
39473 });/*
39474  * Based on:
39475  * Ext JS Library 1.1.1
39476  * Copyright(c) 2006-2007, Ext JS, LLC.
39477  *
39478  * Originally Released Under LGPL - original licence link has changed is not relivant.
39479  *
39480  * Fork - LGPL
39481  * <script type="text/javascript">
39482  */
39483 /**
39484  * @class Roo.menu.Item
39485  * @extends Roo.menu.BaseItem
39486  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39487  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39488  * activation and click handling.
39489  * @constructor
39490  * Creates a new Item
39491  * @param {Object} config Configuration options
39492  */
39493 Roo.menu.Item = function(config){
39494     Roo.menu.Item.superclass.constructor.call(this, config);
39495     if(this.menu){
39496         this.menu = Roo.menu.MenuMgr.get(this.menu);
39497     }
39498 };
39499 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39500     /**
39501      * @cfg {Roo.menu.Menu} menu
39502      * A Sub menu
39503      */
39504     /**
39505      * @cfg {String} text
39506      * The text to show on the menu item.
39507      */
39508     text: '',
39509      /**
39510      * @cfg {String} HTML to render in menu
39511      * The text to show on the menu item (HTML version).
39512      */
39513     html: '',
39514     /**
39515      * @cfg {String} icon
39516      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39517      */
39518     icon: undefined,
39519     /**
39520      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39521      */
39522     itemCls : "x-menu-item",
39523     /**
39524      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39525      */
39526     canActivate : true,
39527     /**
39528      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39529      */
39530     showDelay: 200,
39531     // doc'd in BaseItem
39532     hideDelay: 200,
39533
39534     // private
39535     ctype: "Roo.menu.Item",
39536     
39537     // private
39538     onRender : function(container, position){
39539         var el = document.createElement("a");
39540         el.hideFocus = true;
39541         el.unselectable = "on";
39542         el.href = this.href || "#";
39543         if(this.hrefTarget){
39544             el.target = this.hrefTarget;
39545         }
39546         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39547         
39548         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39549         
39550         el.innerHTML = String.format(
39551                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39552                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39553         this.el = el;
39554         Roo.menu.Item.superclass.onRender.call(this, container, position);
39555     },
39556
39557     /**
39558      * Sets the text to display in this menu item
39559      * @param {String} text The text to display
39560      * @param {Boolean} isHTML true to indicate text is pure html.
39561      */
39562     setText : function(text, isHTML){
39563         if (isHTML) {
39564             this.html = text;
39565         } else {
39566             this.text = text;
39567             this.html = '';
39568         }
39569         if(this.rendered){
39570             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39571      
39572             this.el.update(String.format(
39573                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39574                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39575             this.parentMenu.autoWidth();
39576         }
39577     },
39578
39579     // private
39580     handleClick : function(e){
39581         if(!this.href){ // if no link defined, stop the event automatically
39582             e.stopEvent();
39583         }
39584         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39585     },
39586
39587     // private
39588     activate : function(autoExpand){
39589         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39590             this.focus();
39591             if(autoExpand){
39592                 this.expandMenu();
39593             }
39594         }
39595         return true;
39596     },
39597
39598     // private
39599     shouldDeactivate : function(e){
39600         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39601             if(this.menu && this.menu.isVisible()){
39602                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39603             }
39604             return true;
39605         }
39606         return false;
39607     },
39608
39609     // private
39610     deactivate : function(){
39611         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39612         this.hideMenu();
39613     },
39614
39615     // private
39616     expandMenu : function(autoActivate){
39617         if(!this.disabled && this.menu){
39618             clearTimeout(this.hideTimer);
39619             delete this.hideTimer;
39620             if(!this.menu.isVisible() && !this.showTimer){
39621                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39622             }else if (this.menu.isVisible() && autoActivate){
39623                 this.menu.tryActivate(0, 1);
39624             }
39625         }
39626     },
39627
39628     // private
39629     deferExpand : function(autoActivate){
39630         delete this.showTimer;
39631         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39632         if(autoActivate){
39633             this.menu.tryActivate(0, 1);
39634         }
39635     },
39636
39637     // private
39638     hideMenu : function(){
39639         clearTimeout(this.showTimer);
39640         delete this.showTimer;
39641         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39642             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39643         }
39644     },
39645
39646     // private
39647     deferHide : function(){
39648         delete this.hideTimer;
39649         this.menu.hide();
39650     }
39651 });/*
39652  * Based on:
39653  * Ext JS Library 1.1.1
39654  * Copyright(c) 2006-2007, Ext JS, LLC.
39655  *
39656  * Originally Released Under LGPL - original licence link has changed is not relivant.
39657  *
39658  * Fork - LGPL
39659  * <script type="text/javascript">
39660  */
39661  
39662 /**
39663  * @class Roo.menu.CheckItem
39664  * @extends Roo.menu.Item
39665  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39666  * @constructor
39667  * Creates a new CheckItem
39668  * @param {Object} config Configuration options
39669  */
39670 Roo.menu.CheckItem = function(config){
39671     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39672     this.addEvents({
39673         /**
39674          * @event beforecheckchange
39675          * Fires before the checked value is set, providing an opportunity to cancel if needed
39676          * @param {Roo.menu.CheckItem} this
39677          * @param {Boolean} checked The new checked value that will be set
39678          */
39679         "beforecheckchange" : true,
39680         /**
39681          * @event checkchange
39682          * Fires after the checked value has been set
39683          * @param {Roo.menu.CheckItem} this
39684          * @param {Boolean} checked The checked value that was set
39685          */
39686         "checkchange" : true
39687     });
39688     if(this.checkHandler){
39689         this.on('checkchange', this.checkHandler, this.scope);
39690     }
39691 };
39692 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
39693     /**
39694      * @cfg {String} group
39695      * All check items with the same group name will automatically be grouped into a single-select
39696      * radio button group (defaults to '')
39697      */
39698     /**
39699      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
39700      */
39701     itemCls : "x-menu-item x-menu-check-item",
39702     /**
39703      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
39704      */
39705     groupClass : "x-menu-group-item",
39706
39707     /**
39708      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
39709      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
39710      * initialized with checked = true will be rendered as checked.
39711      */
39712     checked: false,
39713
39714     // private
39715     ctype: "Roo.menu.CheckItem",
39716
39717     // private
39718     onRender : function(c){
39719         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
39720         if(this.group){
39721             this.el.addClass(this.groupClass);
39722         }
39723         Roo.menu.MenuMgr.registerCheckable(this);
39724         if(this.checked){
39725             this.checked = false;
39726             this.setChecked(true, true);
39727         }
39728     },
39729
39730     // private
39731     destroy : function(){
39732         if(this.rendered){
39733             Roo.menu.MenuMgr.unregisterCheckable(this);
39734         }
39735         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
39736     },
39737
39738     /**
39739      * Set the checked state of this item
39740      * @param {Boolean} checked The new checked value
39741      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
39742      */
39743     setChecked : function(state, suppressEvent){
39744         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
39745             if(this.container){
39746                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
39747             }
39748             this.checked = state;
39749             if(suppressEvent !== true){
39750                 this.fireEvent("checkchange", this, state);
39751             }
39752         }
39753     },
39754
39755     // private
39756     handleClick : function(e){
39757        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
39758            this.setChecked(!this.checked);
39759        }
39760        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
39761     }
39762 });/*
39763  * Based on:
39764  * Ext JS Library 1.1.1
39765  * Copyright(c) 2006-2007, Ext JS, LLC.
39766  *
39767  * Originally Released Under LGPL - original licence link has changed is not relivant.
39768  *
39769  * Fork - LGPL
39770  * <script type="text/javascript">
39771  */
39772  
39773 /**
39774  * @class Roo.menu.DateItem
39775  * @extends Roo.menu.Adapter
39776  * A menu item that wraps the {@link Roo.DatPicker} component.
39777  * @constructor
39778  * Creates a new DateItem
39779  * @param {Object} config Configuration options
39780  */
39781 Roo.menu.DateItem = function(config){
39782     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
39783     /** The Roo.DatePicker object @type Roo.DatePicker */
39784     this.picker = this.component;
39785     this.addEvents({select: true});
39786     
39787     this.picker.on("render", function(picker){
39788         picker.getEl().swallowEvent("click");
39789         picker.container.addClass("x-menu-date-item");
39790     });
39791
39792     this.picker.on("select", this.onSelect, this);
39793 };
39794
39795 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
39796     // private
39797     onSelect : function(picker, date){
39798         this.fireEvent("select", this, date, picker);
39799         Roo.menu.DateItem.superclass.handleClick.call(this);
39800     }
39801 });/*
39802  * Based on:
39803  * Ext JS Library 1.1.1
39804  * Copyright(c) 2006-2007, Ext JS, LLC.
39805  *
39806  * Originally Released Under LGPL - original licence link has changed is not relivant.
39807  *
39808  * Fork - LGPL
39809  * <script type="text/javascript">
39810  */
39811  
39812 /**
39813  * @class Roo.menu.ColorItem
39814  * @extends Roo.menu.Adapter
39815  * A menu item that wraps the {@link Roo.ColorPalette} component.
39816  * @constructor
39817  * Creates a new ColorItem
39818  * @param {Object} config Configuration options
39819  */
39820 Roo.menu.ColorItem = function(config){
39821     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
39822     /** The Roo.ColorPalette object @type Roo.ColorPalette */
39823     this.palette = this.component;
39824     this.relayEvents(this.palette, ["select"]);
39825     if(this.selectHandler){
39826         this.on('select', this.selectHandler, this.scope);
39827     }
39828 };
39829 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
39830  * Based on:
39831  * Ext JS Library 1.1.1
39832  * Copyright(c) 2006-2007, Ext JS, LLC.
39833  *
39834  * Originally Released Under LGPL - original licence link has changed is not relivant.
39835  *
39836  * Fork - LGPL
39837  * <script type="text/javascript">
39838  */
39839  
39840
39841 /**
39842  * @class Roo.menu.DateMenu
39843  * @extends Roo.menu.Menu
39844  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
39845  * @constructor
39846  * Creates a new DateMenu
39847  * @param {Object} config Configuration options
39848  */
39849 Roo.menu.DateMenu = function(config){
39850     Roo.menu.DateMenu.superclass.constructor.call(this, config);
39851     this.plain = true;
39852     var di = new Roo.menu.DateItem(config);
39853     this.add(di);
39854     /**
39855      * The {@link Roo.DatePicker} instance for this DateMenu
39856      * @type DatePicker
39857      */
39858     this.picker = di.picker;
39859     /**
39860      * @event select
39861      * @param {DatePicker} picker
39862      * @param {Date} date
39863      */
39864     this.relayEvents(di, ["select"]);
39865     this.on('beforeshow', function(){
39866         if(this.picker){
39867             this.picker.hideMonthPicker(false);
39868         }
39869     }, this);
39870 };
39871 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
39872     cls:'x-date-menu'
39873 });/*
39874  * Based on:
39875  * Ext JS Library 1.1.1
39876  * Copyright(c) 2006-2007, Ext JS, LLC.
39877  *
39878  * Originally Released Under LGPL - original licence link has changed is not relivant.
39879  *
39880  * Fork - LGPL
39881  * <script type="text/javascript">
39882  */
39883  
39884
39885 /**
39886  * @class Roo.menu.ColorMenu
39887  * @extends Roo.menu.Menu
39888  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
39889  * @constructor
39890  * Creates a new ColorMenu
39891  * @param {Object} config Configuration options
39892  */
39893 Roo.menu.ColorMenu = function(config){
39894     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
39895     this.plain = true;
39896     var ci = new Roo.menu.ColorItem(config);
39897     this.add(ci);
39898     /**
39899      * The {@link Roo.ColorPalette} instance for this ColorMenu
39900      * @type ColorPalette
39901      */
39902     this.palette = ci.palette;
39903     /**
39904      * @event select
39905      * @param {ColorPalette} palette
39906      * @param {String} color
39907      */
39908     this.relayEvents(ci, ["select"]);
39909 };
39910 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
39911  * Based on:
39912  * Ext JS Library 1.1.1
39913  * Copyright(c) 2006-2007, Ext JS, LLC.
39914  *
39915  * Originally Released Under LGPL - original licence link has changed is not relivant.
39916  *
39917  * Fork - LGPL
39918  * <script type="text/javascript">
39919  */
39920  
39921 /**
39922  * @class Roo.form.TextItem
39923  * @extends Roo.BoxComponent
39924  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39925  * @constructor
39926  * Creates a new TextItem
39927  * @param {Object} config Configuration options
39928  */
39929 Roo.form.TextItem = function(config){
39930     Roo.form.TextItem.superclass.constructor.call(this, config);
39931 };
39932
39933 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39934     
39935     /**
39936      * @cfg {String} tag the tag for this item (default div)
39937      */
39938     tag : 'div',
39939     /**
39940      * @cfg {String} html the content for this item
39941      */
39942     html : '',
39943     
39944     getAutoCreate : function()
39945     {
39946         var cfg = {
39947             id: this.id,
39948             tag: this.tag,
39949             html: this.html,
39950             cls: 'x-form-item'
39951         };
39952         
39953         return cfg;
39954         
39955     },
39956     
39957     onRender : function(ct, position)
39958     {
39959         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39960         
39961         if(!this.el){
39962             var cfg = this.getAutoCreate();
39963             if(!cfg.name){
39964                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39965             }
39966             if (!cfg.name.length) {
39967                 delete cfg.name;
39968             }
39969             this.el = ct.createChild(cfg, position);
39970         }
39971     },
39972     /*
39973      * setHTML
39974      * @param {String} html update the Contents of the element.
39975      */
39976     setHTML : function(html)
39977     {
39978         this.fieldEl.dom.innerHTML = html;
39979     }
39980     
39981 });/*
39982  * Based on:
39983  * Ext JS Library 1.1.1
39984  * Copyright(c) 2006-2007, Ext JS, LLC.
39985  *
39986  * Originally Released Under LGPL - original licence link has changed is not relivant.
39987  *
39988  * Fork - LGPL
39989  * <script type="text/javascript">
39990  */
39991  
39992 /**
39993  * @class Roo.form.Field
39994  * @extends Roo.BoxComponent
39995  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39996  * @constructor
39997  * Creates a new Field
39998  * @param {Object} config Configuration options
39999  */
40000 Roo.form.Field = function(config){
40001     Roo.form.Field.superclass.constructor.call(this, config);
40002 };
40003
40004 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
40005     /**
40006      * @cfg {String} fieldLabel Label to use when rendering a form.
40007      */
40008        /**
40009      * @cfg {String} qtip Mouse over tip
40010      */
40011      
40012     /**
40013      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40014      */
40015     invalidClass : "x-form-invalid",
40016     /**
40017      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
40018      */
40019     invalidText : "The value in this field is invalid",
40020     /**
40021      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40022      */
40023     focusClass : "x-form-focus",
40024     /**
40025      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40026       automatic validation (defaults to "keyup").
40027      */
40028     validationEvent : "keyup",
40029     /**
40030      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40031      */
40032     validateOnBlur : true,
40033     /**
40034      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40035      */
40036     validationDelay : 250,
40037     /**
40038      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40039      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40040      */
40041     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40042     /**
40043      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40044      */
40045     fieldClass : "x-form-field",
40046     /**
40047      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40048      *<pre>
40049 Value         Description
40050 -----------   ----------------------------------------------------------------------
40051 qtip          Display a quick tip when the user hovers over the field
40052 title         Display a default browser title attribute popup
40053 under         Add a block div beneath the field containing the error text
40054 side          Add an error icon to the right of the field with a popup on hover
40055 [element id]  Add the error text directly to the innerHTML of the specified element
40056 </pre>
40057      */
40058     msgTarget : 'qtip',
40059     /**
40060      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40061      */
40062     msgFx : 'normal',
40063
40064     /**
40065      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
40066      */
40067     readOnly : false,
40068
40069     /**
40070      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40071      */
40072     disabled : false,
40073
40074     /**
40075      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40076      */
40077     inputType : undefined,
40078     
40079     /**
40080      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
40081          */
40082         tabIndex : undefined,
40083         
40084     // private
40085     isFormField : true,
40086
40087     // private
40088     hasFocus : false,
40089     /**
40090      * @property {Roo.Element} fieldEl
40091      * Element Containing the rendered Field (with label etc.)
40092      */
40093     /**
40094      * @cfg {Mixed} value A value to initialize this field with.
40095      */
40096     value : undefined,
40097
40098     /**
40099      * @cfg {String} name The field's HTML name attribute.
40100      */
40101     /**
40102      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40103      */
40104     // private
40105     loadedValue : false,
40106      
40107      
40108         // private ??
40109         initComponent : function(){
40110         Roo.form.Field.superclass.initComponent.call(this);
40111         this.addEvents({
40112             /**
40113              * @event focus
40114              * Fires when this field receives input focus.
40115              * @param {Roo.form.Field} this
40116              */
40117             focus : true,
40118             /**
40119              * @event blur
40120              * Fires when this field loses input focus.
40121              * @param {Roo.form.Field} this
40122              */
40123             blur : true,
40124             /**
40125              * @event specialkey
40126              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40127              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40128              * @param {Roo.form.Field} this
40129              * @param {Roo.EventObject} e The event object
40130              */
40131             specialkey : true,
40132             /**
40133              * @event change
40134              * Fires just before the field blurs if the field value has changed.
40135              * @param {Roo.form.Field} this
40136              * @param {Mixed} newValue The new value
40137              * @param {Mixed} oldValue The original value
40138              */
40139             change : true,
40140             /**
40141              * @event invalid
40142              * Fires after the field has been marked as invalid.
40143              * @param {Roo.form.Field} this
40144              * @param {String} msg The validation message
40145              */
40146             invalid : true,
40147             /**
40148              * @event valid
40149              * Fires after the field has been validated with no errors.
40150              * @param {Roo.form.Field} this
40151              */
40152             valid : true,
40153              /**
40154              * @event keyup
40155              * Fires after the key up
40156              * @param {Roo.form.Field} this
40157              * @param {Roo.EventObject}  e The event Object
40158              */
40159             keyup : true
40160         });
40161     },
40162
40163     /**
40164      * Returns the name attribute of the field if available
40165      * @return {String} name The field name
40166      */
40167     getName: function(){
40168          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40169     },
40170
40171     // private
40172     onRender : function(ct, position){
40173         Roo.form.Field.superclass.onRender.call(this, ct, position);
40174         if(!this.el){
40175             var cfg = this.getAutoCreate();
40176             if(!cfg.name){
40177                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40178             }
40179             if (!cfg.name.length) {
40180                 delete cfg.name;
40181             }
40182             if(this.inputType){
40183                 cfg.type = this.inputType;
40184             }
40185             this.el = ct.createChild(cfg, position);
40186         }
40187         var type = this.el.dom.type;
40188         if(type){
40189             if(type == 'password'){
40190                 type = 'text';
40191             }
40192             this.el.addClass('x-form-'+type);
40193         }
40194         if(this.readOnly){
40195             this.el.dom.readOnly = true;
40196         }
40197         if(this.tabIndex !== undefined){
40198             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40199         }
40200
40201         this.el.addClass([this.fieldClass, this.cls]);
40202         this.initValue();
40203     },
40204
40205     /**
40206      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40207      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40208      * @return {Roo.form.Field} this
40209      */
40210     applyTo : function(target){
40211         this.allowDomMove = false;
40212         this.el = Roo.get(target);
40213         this.render(this.el.dom.parentNode);
40214         return this;
40215     },
40216
40217     // private
40218     initValue : function(){
40219         if(this.value !== undefined){
40220             this.setValue(this.value);
40221         }else if(this.el.dom.value.length > 0){
40222             this.setValue(this.el.dom.value);
40223         }
40224     },
40225
40226     /**
40227      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40228      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40229      */
40230     isDirty : function() {
40231         if(this.disabled) {
40232             return false;
40233         }
40234         return String(this.getValue()) !== String(this.originalValue);
40235     },
40236
40237     /**
40238      * stores the current value in loadedValue
40239      */
40240     resetHasChanged : function()
40241     {
40242         this.loadedValue = String(this.getValue());
40243     },
40244     /**
40245      * checks the current value against the 'loaded' value.
40246      * Note - will return false if 'resetHasChanged' has not been called first.
40247      */
40248     hasChanged : function()
40249     {
40250         if(this.disabled || this.readOnly) {
40251             return false;
40252         }
40253         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40254     },
40255     
40256     
40257     
40258     // private
40259     afterRender : function(){
40260         Roo.form.Field.superclass.afterRender.call(this);
40261         this.initEvents();
40262     },
40263
40264     // private
40265     fireKey : function(e){
40266         //Roo.log('field ' + e.getKey());
40267         if(e.isNavKeyPress()){
40268             this.fireEvent("specialkey", this, e);
40269         }
40270     },
40271
40272     /**
40273      * Resets the current field value to the originally loaded value and clears any validation messages
40274      */
40275     reset : function(){
40276         this.setValue(this.resetValue);
40277         this.originalValue = this.getValue();
40278         this.clearInvalid();
40279     },
40280
40281     // private
40282     initEvents : function(){
40283         // safari killled keypress - so keydown is now used..
40284         this.el.on("keydown" , this.fireKey,  this);
40285         this.el.on("focus", this.onFocus,  this);
40286         this.el.on("blur", this.onBlur,  this);
40287         this.el.relayEvent('keyup', this);
40288
40289         // reference to original value for reset
40290         this.originalValue = this.getValue();
40291         this.resetValue =  this.getValue();
40292     },
40293
40294     // private
40295     onFocus : function(){
40296         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40297             this.el.addClass(this.focusClass);
40298         }
40299         if(!this.hasFocus){
40300             this.hasFocus = true;
40301             this.startValue = this.getValue();
40302             this.fireEvent("focus", this);
40303         }
40304     },
40305
40306     beforeBlur : Roo.emptyFn,
40307
40308     // private
40309     onBlur : function(){
40310         this.beforeBlur();
40311         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40312             this.el.removeClass(this.focusClass);
40313         }
40314         this.hasFocus = false;
40315         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40316             this.validate();
40317         }
40318         var v = this.getValue();
40319         if(String(v) !== String(this.startValue)){
40320             this.fireEvent('change', this, v, this.startValue);
40321         }
40322         this.fireEvent("blur", this);
40323     },
40324
40325     /**
40326      * Returns whether or not the field value is currently valid
40327      * @param {Boolean} preventMark True to disable marking the field invalid
40328      * @return {Boolean} True if the value is valid, else false
40329      */
40330     isValid : function(preventMark){
40331         if(this.disabled){
40332             return true;
40333         }
40334         var restore = this.preventMark;
40335         this.preventMark = preventMark === true;
40336         var v = this.validateValue(this.processValue(this.getRawValue()));
40337         this.preventMark = restore;
40338         return v;
40339     },
40340
40341     /**
40342      * Validates the field value
40343      * @return {Boolean} True if the value is valid, else false
40344      */
40345     validate : function(){
40346         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40347             this.clearInvalid();
40348             return true;
40349         }
40350         return false;
40351     },
40352
40353     processValue : function(value){
40354         return value;
40355     },
40356
40357     // private
40358     // Subclasses should provide the validation implementation by overriding this
40359     validateValue : function(value){
40360         return true;
40361     },
40362
40363     /**
40364      * Mark this field as invalid
40365      * @param {String} msg The validation message
40366      */
40367     markInvalid : function(msg){
40368         if(!this.rendered || this.preventMark){ // not rendered
40369             return;
40370         }
40371         
40372         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40373         
40374         obj.el.addClass(this.invalidClass);
40375         msg = msg || this.invalidText;
40376         switch(this.msgTarget){
40377             case 'qtip':
40378                 obj.el.dom.qtip = msg;
40379                 obj.el.dom.qclass = 'x-form-invalid-tip';
40380                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40381                     Roo.QuickTips.enable();
40382                 }
40383                 break;
40384             case 'title':
40385                 this.el.dom.title = msg;
40386                 break;
40387             case 'under':
40388                 if(!this.errorEl){
40389                     var elp = this.el.findParent('.x-form-element', 5, true);
40390                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40391                     this.errorEl.setWidth(elp.getWidth(true)-20);
40392                 }
40393                 this.errorEl.update(msg);
40394                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40395                 break;
40396             case 'side':
40397                 if(!this.errorIcon){
40398                     var elp = this.el.findParent('.x-form-element', 5, true);
40399                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40400                 }
40401                 this.alignErrorIcon();
40402                 this.errorIcon.dom.qtip = msg;
40403                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40404                 this.errorIcon.show();
40405                 this.on('resize', this.alignErrorIcon, this);
40406                 break;
40407             default:
40408                 var t = Roo.getDom(this.msgTarget);
40409                 t.innerHTML = msg;
40410                 t.style.display = this.msgDisplay;
40411                 break;
40412         }
40413         this.fireEvent('invalid', this, msg);
40414     },
40415
40416     // private
40417     alignErrorIcon : function(){
40418         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40419     },
40420
40421     /**
40422      * Clear any invalid styles/messages for this field
40423      */
40424     clearInvalid : function(){
40425         if(!this.rendered || this.preventMark){ // not rendered
40426             return;
40427         }
40428         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40429         
40430         obj.el.removeClass(this.invalidClass);
40431         switch(this.msgTarget){
40432             case 'qtip':
40433                 obj.el.dom.qtip = '';
40434                 break;
40435             case 'title':
40436                 this.el.dom.title = '';
40437                 break;
40438             case 'under':
40439                 if(this.errorEl){
40440                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40441                 }
40442                 break;
40443             case 'side':
40444                 if(this.errorIcon){
40445                     this.errorIcon.dom.qtip = '';
40446                     this.errorIcon.hide();
40447                     this.un('resize', this.alignErrorIcon, this);
40448                 }
40449                 break;
40450             default:
40451                 var t = Roo.getDom(this.msgTarget);
40452                 t.innerHTML = '';
40453                 t.style.display = 'none';
40454                 break;
40455         }
40456         this.fireEvent('valid', this);
40457     },
40458
40459     /**
40460      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40461      * @return {Mixed} value The field value
40462      */
40463     getRawValue : function(){
40464         var v = this.el.getValue();
40465         
40466         return v;
40467     },
40468
40469     /**
40470      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40471      * @return {Mixed} value The field value
40472      */
40473     getValue : function(){
40474         var v = this.el.getValue();
40475          
40476         return v;
40477     },
40478
40479     /**
40480      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40481      * @param {Mixed} value The value to set
40482      */
40483     setRawValue : function(v){
40484         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40485     },
40486
40487     /**
40488      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40489      * @param {Mixed} value The value to set
40490      */
40491     setValue : function(v){
40492         this.value = v;
40493         if(this.rendered){
40494             this.el.dom.value = (v === null || v === undefined ? '' : v);
40495              this.validate();
40496         }
40497     },
40498
40499     adjustSize : function(w, h){
40500         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40501         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40502         return s;
40503     },
40504
40505     adjustWidth : function(tag, w){
40506         tag = tag.toLowerCase();
40507         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40508             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40509                 if(tag == 'input'){
40510                     return w + 2;
40511                 }
40512                 if(tag == 'textarea'){
40513                     return w-2;
40514                 }
40515             }else if(Roo.isOpera){
40516                 if(tag == 'input'){
40517                     return w + 2;
40518                 }
40519                 if(tag == 'textarea'){
40520                     return w-2;
40521                 }
40522             }
40523         }
40524         return w;
40525     }
40526 });
40527
40528
40529 // anything other than normal should be considered experimental
40530 Roo.form.Field.msgFx = {
40531     normal : {
40532         show: function(msgEl, f){
40533             msgEl.setDisplayed('block');
40534         },
40535
40536         hide : function(msgEl, f){
40537             msgEl.setDisplayed(false).update('');
40538         }
40539     },
40540
40541     slide : {
40542         show: function(msgEl, f){
40543             msgEl.slideIn('t', {stopFx:true});
40544         },
40545
40546         hide : function(msgEl, f){
40547             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40548         }
40549     },
40550
40551     slideRight : {
40552         show: function(msgEl, f){
40553             msgEl.fixDisplay();
40554             msgEl.alignTo(f.el, 'tl-tr');
40555             msgEl.slideIn('l', {stopFx:true});
40556         },
40557
40558         hide : function(msgEl, f){
40559             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40560         }
40561     }
40562 };/*
40563  * Based on:
40564  * Ext JS Library 1.1.1
40565  * Copyright(c) 2006-2007, Ext JS, LLC.
40566  *
40567  * Originally Released Under LGPL - original licence link has changed is not relivant.
40568  *
40569  * Fork - LGPL
40570  * <script type="text/javascript">
40571  */
40572  
40573
40574 /**
40575  * @class Roo.form.TextField
40576  * @extends Roo.form.Field
40577  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40578  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40579  * @constructor
40580  * Creates a new TextField
40581  * @param {Object} config Configuration options
40582  */
40583 Roo.form.TextField = function(config){
40584     Roo.form.TextField.superclass.constructor.call(this, config);
40585     this.addEvents({
40586         /**
40587          * @event autosize
40588          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40589          * according to the default logic, but this event provides a hook for the developer to apply additional
40590          * logic at runtime to resize the field if needed.
40591              * @param {Roo.form.Field} this This text field
40592              * @param {Number} width The new field width
40593              */
40594         autosize : true
40595     });
40596 };
40597
40598 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40599     /**
40600      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40601      */
40602     grow : false,
40603     /**
40604      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40605      */
40606     growMin : 30,
40607     /**
40608      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40609      */
40610     growMax : 800,
40611     /**
40612      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40613      */
40614     vtype : null,
40615     /**
40616      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40617      */
40618     maskRe : null,
40619     /**
40620      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40621      */
40622     disableKeyFilter : false,
40623     /**
40624      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40625      */
40626     allowBlank : true,
40627     /**
40628      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40629      */
40630     minLength : 0,
40631     /**
40632      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40633      */
40634     maxLength : Number.MAX_VALUE,
40635     /**
40636      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40637      */
40638     minLengthText : "The minimum length for this field is {0}",
40639     /**
40640      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40641      */
40642     maxLengthText : "The maximum length for this field is {0}",
40643     /**
40644      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40645      */
40646     selectOnFocus : false,
40647     /**
40648      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40649      */    
40650     allowLeadingSpace : false,
40651     /**
40652      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40653      */
40654     blankText : "This field is required",
40655     /**
40656      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40657      * If available, this function will be called only after the basic validators all return true, and will be passed the
40658      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40659      */
40660     validator : null,
40661     /**
40662      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40663      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40664      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40665      */
40666     regex : null,
40667     /**
40668      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40669      */
40670     regexText : "",
40671     /**
40672      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40673      */
40674     emptyText : null,
40675    
40676
40677     // private
40678     initEvents : function()
40679     {
40680         if (this.emptyText) {
40681             this.el.attr('placeholder', this.emptyText);
40682         }
40683         
40684         Roo.form.TextField.superclass.initEvents.call(this);
40685         if(this.validationEvent == 'keyup'){
40686             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40687             this.el.on('keyup', this.filterValidation, this);
40688         }
40689         else if(this.validationEvent !== false){
40690             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40691         }
40692         
40693         if(this.selectOnFocus){
40694             this.on("focus", this.preFocus, this);
40695         }
40696         if (!this.allowLeadingSpace) {
40697             this.on('blur', this.cleanLeadingSpace, this);
40698         }
40699         
40700         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40701             this.el.on("keypress", this.filterKeys, this);
40702         }
40703         if(this.grow){
40704             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
40705             this.el.on("click", this.autoSize,  this);
40706         }
40707         if(this.el.is('input[type=password]') && Roo.isSafari){
40708             this.el.on('keydown', this.SafariOnKeyDown, this);
40709         }
40710     },
40711
40712     processValue : function(value){
40713         if(this.stripCharsRe){
40714             var newValue = value.replace(this.stripCharsRe, '');
40715             if(newValue !== value){
40716                 this.setRawValue(newValue);
40717                 return newValue;
40718             }
40719         }
40720         return value;
40721     },
40722
40723     filterValidation : function(e){
40724         if(!e.isNavKeyPress()){
40725             this.validationTask.delay(this.validationDelay);
40726         }
40727     },
40728
40729     // private
40730     onKeyUp : function(e){
40731         if(!e.isNavKeyPress()){
40732             this.autoSize();
40733         }
40734     },
40735     // private - clean the leading white space
40736     cleanLeadingSpace : function(e)
40737     {
40738         if ( this.inputType == 'file') {
40739             return;
40740         }
40741         
40742         this.setValue((this.getValue() + '').replace(/^\s+/,''));
40743     },
40744     /**
40745      * Resets the current field value to the originally-loaded value and clears any validation messages.
40746      *  
40747      */
40748     reset : function(){
40749         Roo.form.TextField.superclass.reset.call(this);
40750        
40751     }, 
40752     // private
40753     preFocus : function(){
40754         
40755         if(this.selectOnFocus){
40756             this.el.dom.select();
40757         }
40758     },
40759
40760     
40761     // private
40762     filterKeys : function(e){
40763         var k = e.getKey();
40764         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
40765             return;
40766         }
40767         var c = e.getCharCode(), cc = String.fromCharCode(c);
40768         if(Roo.isIE && (e.isSpecialKey() || !cc)){
40769             return;
40770         }
40771         if(!this.maskRe.test(cc)){
40772             e.stopEvent();
40773         }
40774     },
40775
40776     setValue : function(v){
40777         
40778         Roo.form.TextField.superclass.setValue.apply(this, arguments);
40779         
40780         this.autoSize();
40781     },
40782
40783     /**
40784      * Validates a value according to the field's validation rules and marks the field as invalid
40785      * if the validation fails
40786      * @param {Mixed} value The value to validate
40787      * @return {Boolean} True if the value is valid, else false
40788      */
40789     validateValue : function(value){
40790         if(value.length < 1)  { // if it's blank
40791              if(this.allowBlank){
40792                 this.clearInvalid();
40793                 return true;
40794              }else{
40795                 this.markInvalid(this.blankText);
40796                 return false;
40797              }
40798         }
40799         if(value.length < this.minLength){
40800             this.markInvalid(String.format(this.minLengthText, this.minLength));
40801             return false;
40802         }
40803         if(value.length > this.maxLength){
40804             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
40805             return false;
40806         }
40807         if(this.vtype){
40808             var vt = Roo.form.VTypes;
40809             if(!vt[this.vtype](value, this)){
40810                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
40811                 return false;
40812             }
40813         }
40814         if(typeof this.validator == "function"){
40815             var msg = this.validator(value);
40816             if(msg !== true){
40817                 this.markInvalid(msg);
40818                 return false;
40819             }
40820         }
40821         if(this.regex && !this.regex.test(value)){
40822             this.markInvalid(this.regexText);
40823             return false;
40824         }
40825         return true;
40826     },
40827
40828     /**
40829      * Selects text in this field
40830      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
40831      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
40832      */
40833     selectText : function(start, end){
40834         var v = this.getRawValue();
40835         if(v.length > 0){
40836             start = start === undefined ? 0 : start;
40837             end = end === undefined ? v.length : end;
40838             var d = this.el.dom;
40839             if(d.setSelectionRange){
40840                 d.setSelectionRange(start, end);
40841             }else if(d.createTextRange){
40842                 var range = d.createTextRange();
40843                 range.moveStart("character", start);
40844                 range.moveEnd("character", v.length-end);
40845                 range.select();
40846             }
40847         }
40848     },
40849
40850     /**
40851      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
40852      * This only takes effect if grow = true, and fires the autosize event.
40853      */
40854     autoSize : function(){
40855         if(!this.grow || !this.rendered){
40856             return;
40857         }
40858         if(!this.metrics){
40859             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
40860         }
40861         var el = this.el;
40862         var v = el.dom.value;
40863         var d = document.createElement('div');
40864         d.appendChild(document.createTextNode(v));
40865         v = d.innerHTML;
40866         d = null;
40867         v += "&#160;";
40868         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
40869         this.el.setWidth(w);
40870         this.fireEvent("autosize", this, w);
40871     },
40872     
40873     // private
40874     SafariOnKeyDown : function(event)
40875     {
40876         // this is a workaround for a password hang bug on chrome/ webkit.
40877         
40878         var isSelectAll = false;
40879         
40880         if(this.el.dom.selectionEnd > 0){
40881             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
40882         }
40883         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
40884             event.preventDefault();
40885             this.setValue('');
40886             return;
40887         }
40888         
40889         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
40890             
40891             event.preventDefault();
40892             // this is very hacky as keydown always get's upper case.
40893             
40894             var cc = String.fromCharCode(event.getCharCode());
40895             
40896             
40897             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
40898             
40899         }
40900         
40901         
40902     }
40903 });/*
40904  * Based on:
40905  * Ext JS Library 1.1.1
40906  * Copyright(c) 2006-2007, Ext JS, LLC.
40907  *
40908  * Originally Released Under LGPL - original licence link has changed is not relivant.
40909  *
40910  * Fork - LGPL
40911  * <script type="text/javascript">
40912  */
40913  
40914 /**
40915  * @class Roo.form.Hidden
40916  * @extends Roo.form.TextField
40917  * Simple Hidden element used on forms 
40918  * 
40919  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
40920  * 
40921  * @constructor
40922  * Creates a new Hidden form element.
40923  * @param {Object} config Configuration options
40924  */
40925
40926
40927
40928 // easy hidden field...
40929 Roo.form.Hidden = function(config){
40930     Roo.form.Hidden.superclass.constructor.call(this, config);
40931 };
40932   
40933 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40934     fieldLabel:      '',
40935     inputType:      'hidden',
40936     width:          50,
40937     allowBlank:     true,
40938     labelSeparator: '',
40939     hidden:         true,
40940     itemCls :       'x-form-item-display-none'
40941
40942
40943 });
40944
40945
40946 /*
40947  * Based on:
40948  * Ext JS Library 1.1.1
40949  * Copyright(c) 2006-2007, Ext JS, LLC.
40950  *
40951  * Originally Released Under LGPL - original licence link has changed is not relivant.
40952  *
40953  * Fork - LGPL
40954  * <script type="text/javascript">
40955  */
40956  
40957 /**
40958  * @class Roo.form.TriggerField
40959  * @extends Roo.form.TextField
40960  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40961  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40962  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40963  * for which you can provide a custom implementation.  For example:
40964  * <pre><code>
40965 var trigger = new Roo.form.TriggerField();
40966 trigger.onTriggerClick = myTriggerFn;
40967 trigger.applyTo('my-field');
40968 </code></pre>
40969  *
40970  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40971  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40972  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40973  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40974  * @constructor
40975  * Create a new TriggerField.
40976  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40977  * to the base TextField)
40978  */
40979 Roo.form.TriggerField = function(config){
40980     this.mimicing = false;
40981     Roo.form.TriggerField.superclass.constructor.call(this, config);
40982 };
40983
40984 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40985     /**
40986      * @cfg {String} triggerClass A CSS class to apply to the trigger
40987      */
40988     /**
40989      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40990      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40991      */
40992     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40993     /**
40994      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40995      */
40996     hideTrigger:false,
40997
40998     /** @cfg {Boolean} grow @hide */
40999     /** @cfg {Number} growMin @hide */
41000     /** @cfg {Number} growMax @hide */
41001
41002     /**
41003      * @hide 
41004      * @method
41005      */
41006     autoSize: Roo.emptyFn,
41007     // private
41008     monitorTab : true,
41009     // private
41010     deferHeight : true,
41011
41012     
41013     actionMode : 'wrap',
41014     // private
41015     onResize : function(w, h){
41016         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41017         if(typeof w == 'number'){
41018             var x = w - this.trigger.getWidth();
41019             this.el.setWidth(this.adjustWidth('input', x));
41020             this.trigger.setStyle('left', x+'px');
41021         }
41022     },
41023
41024     // private
41025     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41026
41027     // private
41028     getResizeEl : function(){
41029         return this.wrap;
41030     },
41031
41032     // private
41033     getPositionEl : function(){
41034         return this.wrap;
41035     },
41036
41037     // private
41038     alignErrorIcon : function(){
41039         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41040     },
41041
41042     // private
41043     onRender : function(ct, position){
41044         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41045         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41046         this.trigger = this.wrap.createChild(this.triggerConfig ||
41047                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41048         if(this.hideTrigger){
41049             this.trigger.setDisplayed(false);
41050         }
41051         this.initTrigger();
41052         if(!this.width){
41053             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41054         }
41055     },
41056
41057     // private
41058     initTrigger : function(){
41059         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41060         this.trigger.addClassOnOver('x-form-trigger-over');
41061         this.trigger.addClassOnClick('x-form-trigger-click');
41062     },
41063
41064     // private
41065     onDestroy : function(){
41066         if(this.trigger){
41067             this.trigger.removeAllListeners();
41068             this.trigger.remove();
41069         }
41070         if(this.wrap){
41071             this.wrap.remove();
41072         }
41073         Roo.form.TriggerField.superclass.onDestroy.call(this);
41074     },
41075
41076     // private
41077     onFocus : function(){
41078         Roo.form.TriggerField.superclass.onFocus.call(this);
41079         if(!this.mimicing){
41080             this.wrap.addClass('x-trigger-wrap-focus');
41081             this.mimicing = true;
41082             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41083             if(this.monitorTab){
41084                 this.el.on("keydown", this.checkTab, this);
41085             }
41086         }
41087     },
41088
41089     // private
41090     checkTab : function(e){
41091         if(e.getKey() == e.TAB){
41092             this.triggerBlur();
41093         }
41094     },
41095
41096     // private
41097     onBlur : function(){
41098         // do nothing
41099     },
41100
41101     // private
41102     mimicBlur : function(e, t){
41103         if(!this.wrap.contains(t) && this.validateBlur()){
41104             this.triggerBlur();
41105         }
41106     },
41107
41108     // private
41109     triggerBlur : function(){
41110         this.mimicing = false;
41111         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41112         if(this.monitorTab){
41113             this.el.un("keydown", this.checkTab, this);
41114         }
41115         this.wrap.removeClass('x-trigger-wrap-focus');
41116         Roo.form.TriggerField.superclass.onBlur.call(this);
41117     },
41118
41119     // private
41120     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41121     validateBlur : function(e, t){
41122         return true;
41123     },
41124
41125     // private
41126     onDisable : function(){
41127         Roo.form.TriggerField.superclass.onDisable.call(this);
41128         if(this.wrap){
41129             this.wrap.addClass('x-item-disabled');
41130         }
41131     },
41132
41133     // private
41134     onEnable : function(){
41135         Roo.form.TriggerField.superclass.onEnable.call(this);
41136         if(this.wrap){
41137             this.wrap.removeClass('x-item-disabled');
41138         }
41139     },
41140
41141     // private
41142     onShow : function(){
41143         var ae = this.getActionEl();
41144         
41145         if(ae){
41146             ae.dom.style.display = '';
41147             ae.dom.style.visibility = 'visible';
41148         }
41149     },
41150
41151     // private
41152     
41153     onHide : function(){
41154         var ae = this.getActionEl();
41155         ae.dom.style.display = 'none';
41156     },
41157
41158     /**
41159      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41160      * by an implementing function.
41161      * @method
41162      * @param {EventObject} e
41163      */
41164     onTriggerClick : Roo.emptyFn
41165 });
41166
41167 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41168 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41169 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41170 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41171     initComponent : function(){
41172         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41173
41174         this.triggerConfig = {
41175             tag:'span', cls:'x-form-twin-triggers', cn:[
41176             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41177             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41178         ]};
41179     },
41180
41181     getTrigger : function(index){
41182         return this.triggers[index];
41183     },
41184
41185     initTrigger : function(){
41186         var ts = this.trigger.select('.x-form-trigger', true);
41187         this.wrap.setStyle('overflow', 'hidden');
41188         var triggerField = this;
41189         ts.each(function(t, all, index){
41190             t.hide = function(){
41191                 var w = triggerField.wrap.getWidth();
41192                 this.dom.style.display = 'none';
41193                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41194             };
41195             t.show = function(){
41196                 var w = triggerField.wrap.getWidth();
41197                 this.dom.style.display = '';
41198                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41199             };
41200             var triggerIndex = 'Trigger'+(index+1);
41201
41202             if(this['hide'+triggerIndex]){
41203                 t.dom.style.display = 'none';
41204             }
41205             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41206             t.addClassOnOver('x-form-trigger-over');
41207             t.addClassOnClick('x-form-trigger-click');
41208         }, this);
41209         this.triggers = ts.elements;
41210     },
41211
41212     onTrigger1Click : Roo.emptyFn,
41213     onTrigger2Click : Roo.emptyFn
41214 });/*
41215  * Based on:
41216  * Ext JS Library 1.1.1
41217  * Copyright(c) 2006-2007, Ext JS, LLC.
41218  *
41219  * Originally Released Under LGPL - original licence link has changed is not relivant.
41220  *
41221  * Fork - LGPL
41222  * <script type="text/javascript">
41223  */
41224  
41225 /**
41226  * @class Roo.form.TextArea
41227  * @extends Roo.form.TextField
41228  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41229  * support for auto-sizing.
41230  * @constructor
41231  * Creates a new TextArea
41232  * @param {Object} config Configuration options
41233  */
41234 Roo.form.TextArea = function(config){
41235     Roo.form.TextArea.superclass.constructor.call(this, config);
41236     // these are provided exchanges for backwards compat
41237     // minHeight/maxHeight were replaced by growMin/growMax to be
41238     // compatible with TextField growing config values
41239     if(this.minHeight !== undefined){
41240         this.growMin = this.minHeight;
41241     }
41242     if(this.maxHeight !== undefined){
41243         this.growMax = this.maxHeight;
41244     }
41245 };
41246
41247 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41248     /**
41249      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41250      */
41251     growMin : 60,
41252     /**
41253      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41254      */
41255     growMax: 1000,
41256     /**
41257      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41258      * in the field (equivalent to setting overflow: hidden, defaults to false)
41259      */
41260     preventScrollbars: false,
41261     /**
41262      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41263      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41264      */
41265
41266     // private
41267     onRender : function(ct, position){
41268         if(!this.el){
41269             this.defaultAutoCreate = {
41270                 tag: "textarea",
41271                 style:"width:300px;height:60px;",
41272                 autocomplete: "new-password"
41273             };
41274         }
41275         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41276         if(this.grow){
41277             this.textSizeEl = Roo.DomHelper.append(document.body, {
41278                 tag: "pre", cls: "x-form-grow-sizer"
41279             });
41280             if(this.preventScrollbars){
41281                 this.el.setStyle("overflow", "hidden");
41282             }
41283             this.el.setHeight(this.growMin);
41284         }
41285     },
41286
41287     onDestroy : function(){
41288         if(this.textSizeEl){
41289             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41290         }
41291         Roo.form.TextArea.superclass.onDestroy.call(this);
41292     },
41293
41294     // private
41295     onKeyUp : function(e){
41296         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41297             this.autoSize();
41298         }
41299     },
41300
41301     /**
41302      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41303      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41304      */
41305     autoSize : function(){
41306         if(!this.grow || !this.textSizeEl){
41307             return;
41308         }
41309         var el = this.el;
41310         var v = el.dom.value;
41311         var ts = this.textSizeEl;
41312
41313         ts.innerHTML = '';
41314         ts.appendChild(document.createTextNode(v));
41315         v = ts.innerHTML;
41316
41317         Roo.fly(ts).setWidth(this.el.getWidth());
41318         if(v.length < 1){
41319             v = "&#160;&#160;";
41320         }else{
41321             if(Roo.isIE){
41322                 v = v.replace(/\n/g, '<p>&#160;</p>');
41323             }
41324             v += "&#160;\n&#160;";
41325         }
41326         ts.innerHTML = v;
41327         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41328         if(h != this.lastHeight){
41329             this.lastHeight = h;
41330             this.el.setHeight(h);
41331             this.fireEvent("autosize", this, h);
41332         }
41333     }
41334 });/*
41335  * Based on:
41336  * Ext JS Library 1.1.1
41337  * Copyright(c) 2006-2007, Ext JS, LLC.
41338  *
41339  * Originally Released Under LGPL - original licence link has changed is not relivant.
41340  *
41341  * Fork - LGPL
41342  * <script type="text/javascript">
41343  */
41344  
41345
41346 /**
41347  * @class Roo.form.NumberField
41348  * @extends Roo.form.TextField
41349  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41350  * @constructor
41351  * Creates a new NumberField
41352  * @param {Object} config Configuration options
41353  */
41354 Roo.form.NumberField = function(config){
41355     Roo.form.NumberField.superclass.constructor.call(this, config);
41356 };
41357
41358 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41359     /**
41360      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41361      */
41362     fieldClass: "x-form-field x-form-num-field",
41363     /**
41364      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41365      */
41366     allowDecimals : true,
41367     /**
41368      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41369      */
41370     decimalSeparator : ".",
41371     /**
41372      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41373      */
41374     decimalPrecision : 2,
41375     /**
41376      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41377      */
41378     allowNegative : true,
41379     /**
41380      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41381      */
41382     minValue : Number.NEGATIVE_INFINITY,
41383     /**
41384      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41385      */
41386     maxValue : Number.MAX_VALUE,
41387     /**
41388      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41389      */
41390     minText : "The minimum value for this field is {0}",
41391     /**
41392      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41393      */
41394     maxText : "The maximum value for this field is {0}",
41395     /**
41396      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41397      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41398      */
41399     nanText : "{0} is not a valid number",
41400
41401     // private
41402     initEvents : function(){
41403         Roo.form.NumberField.superclass.initEvents.call(this);
41404         var allowed = "0123456789";
41405         if(this.allowDecimals){
41406             allowed += this.decimalSeparator;
41407         }
41408         if(this.allowNegative){
41409             allowed += "-";
41410         }
41411         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41412         var keyPress = function(e){
41413             var k = e.getKey();
41414             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41415                 return;
41416             }
41417             var c = e.getCharCode();
41418             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41419                 e.stopEvent();
41420             }
41421         };
41422         this.el.on("keypress", keyPress, this);
41423     },
41424
41425     // private
41426     validateValue : function(value){
41427         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41428             return false;
41429         }
41430         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41431              return true;
41432         }
41433         var num = this.parseValue(value);
41434         if(isNaN(num)){
41435             this.markInvalid(String.format(this.nanText, value));
41436             return false;
41437         }
41438         if(num < this.minValue){
41439             this.markInvalid(String.format(this.minText, this.minValue));
41440             return false;
41441         }
41442         if(num > this.maxValue){
41443             this.markInvalid(String.format(this.maxText, this.maxValue));
41444             return false;
41445         }
41446         return true;
41447     },
41448
41449     getValue : function(){
41450         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41451     },
41452
41453     // private
41454     parseValue : function(value){
41455         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41456         return isNaN(value) ? '' : value;
41457     },
41458
41459     // private
41460     fixPrecision : function(value){
41461         var nan = isNaN(value);
41462         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41463             return nan ? '' : value;
41464         }
41465         return parseFloat(value).toFixed(this.decimalPrecision);
41466     },
41467
41468     setValue : function(v){
41469         v = this.fixPrecision(v);
41470         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41471     },
41472
41473     // private
41474     decimalPrecisionFcn : function(v){
41475         return Math.floor(v);
41476     },
41477
41478     beforeBlur : function(){
41479         var v = this.parseValue(this.getRawValue());
41480         if(v){
41481             this.setValue(v);
41482         }
41483     }
41484 });/*
41485  * Based on:
41486  * Ext JS Library 1.1.1
41487  * Copyright(c) 2006-2007, Ext JS, LLC.
41488  *
41489  * Originally Released Under LGPL - original licence link has changed is not relivant.
41490  *
41491  * Fork - LGPL
41492  * <script type="text/javascript">
41493  */
41494  
41495 /**
41496  * @class Roo.form.DateField
41497  * @extends Roo.form.TriggerField
41498  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41499 * @constructor
41500 * Create a new DateField
41501 * @param {Object} config
41502  */
41503 Roo.form.DateField = function(config)
41504 {
41505     Roo.form.DateField.superclass.constructor.call(this, config);
41506     
41507       this.addEvents({
41508          
41509         /**
41510          * @event select
41511          * Fires when a date is selected
41512              * @param {Roo.form.DateField} combo This combo box
41513              * @param {Date} date The date selected
41514              */
41515         'select' : true
41516          
41517     });
41518     
41519     
41520     if(typeof this.minValue == "string") {
41521         this.minValue = this.parseDate(this.minValue);
41522     }
41523     if(typeof this.maxValue == "string") {
41524         this.maxValue = this.parseDate(this.maxValue);
41525     }
41526     this.ddMatch = null;
41527     if(this.disabledDates){
41528         var dd = this.disabledDates;
41529         var re = "(?:";
41530         for(var i = 0; i < dd.length; i++){
41531             re += dd[i];
41532             if(i != dd.length-1) {
41533                 re += "|";
41534             }
41535         }
41536         this.ddMatch = new RegExp(re + ")");
41537     }
41538 };
41539
41540 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41541     /**
41542      * @cfg {String} format
41543      * The default date format string which can be overriden for localization support.  The format must be
41544      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41545      */
41546     format : "m/d/y",
41547     /**
41548      * @cfg {String} altFormats
41549      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41550      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41551      */
41552     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41553     /**
41554      * @cfg {Array} disabledDays
41555      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41556      */
41557     disabledDays : null,
41558     /**
41559      * @cfg {String} disabledDaysText
41560      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41561      */
41562     disabledDaysText : "Disabled",
41563     /**
41564      * @cfg {Array} disabledDates
41565      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41566      * expression so they are very powerful. Some examples:
41567      * <ul>
41568      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41569      * <li>["03/08", "09/16"] would disable those days for every year</li>
41570      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41571      * <li>["03/../2006"] would disable every day in March 2006</li>
41572      * <li>["^03"] would disable every day in every March</li>
41573      * </ul>
41574      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41575      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41576      */
41577     disabledDates : null,
41578     /**
41579      * @cfg {String} disabledDatesText
41580      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41581      */
41582     disabledDatesText : "Disabled",
41583         
41584         
41585         /**
41586      * @cfg {Date/String} zeroValue
41587      * if the date is less that this number, then the field is rendered as empty
41588      * default is 1800
41589      */
41590         zeroValue : '1800-01-01',
41591         
41592         
41593     /**
41594      * @cfg {Date/String} minValue
41595      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41596      * valid format (defaults to null).
41597      */
41598     minValue : null,
41599     /**
41600      * @cfg {Date/String} maxValue
41601      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41602      * valid format (defaults to null).
41603      */
41604     maxValue : null,
41605     /**
41606      * @cfg {String} minText
41607      * The error text to display when the date in the cell is before minValue (defaults to
41608      * 'The date in this field must be after {minValue}').
41609      */
41610     minText : "The date in this field must be equal to or after {0}",
41611     /**
41612      * @cfg {String} maxText
41613      * The error text to display when the date in the cell is after maxValue (defaults to
41614      * 'The date in this field must be before {maxValue}').
41615      */
41616     maxText : "The date in this field must be equal to or before {0}",
41617     /**
41618      * @cfg {String} invalidText
41619      * The error text to display when the date in the field is invalid (defaults to
41620      * '{value} is not a valid date - it must be in the format {format}').
41621      */
41622     invalidText : "{0} is not a valid date - it must be in the format {1}",
41623     /**
41624      * @cfg {String} triggerClass
41625      * An additional CSS class used to style the trigger button.  The trigger will always get the
41626      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41627      * which displays a calendar icon).
41628      */
41629     triggerClass : 'x-form-date-trigger',
41630     
41631
41632     /**
41633      * @cfg {Boolean} useIso
41634      * if enabled, then the date field will use a hidden field to store the 
41635      * real value as iso formated date. default (false)
41636      */ 
41637     useIso : false,
41638     /**
41639      * @cfg {String/Object} autoCreate
41640      * A DomHelper element spec, or true for a default element spec (defaults to
41641      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41642      */ 
41643     // private
41644     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41645     
41646     // private
41647     hiddenField: false,
41648     
41649     onRender : function(ct, position)
41650     {
41651         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41652         if (this.useIso) {
41653             //this.el.dom.removeAttribute('name'); 
41654             Roo.log("Changing name?");
41655             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41656             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41657                     'before', true);
41658             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41659             // prevent input submission
41660             this.hiddenName = this.name;
41661         }
41662             
41663             
41664     },
41665     
41666     // private
41667     validateValue : function(value)
41668     {
41669         value = this.formatDate(value);
41670         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41671             Roo.log('super failed');
41672             return false;
41673         }
41674         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41675              return true;
41676         }
41677         var svalue = value;
41678         value = this.parseDate(value);
41679         if(!value){
41680             Roo.log('parse date failed' + svalue);
41681             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41682             return false;
41683         }
41684         var time = value.getTime();
41685         if(this.minValue && time < this.minValue.getTime()){
41686             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41687             return false;
41688         }
41689         if(this.maxValue && time > this.maxValue.getTime()){
41690             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41691             return false;
41692         }
41693         if(this.disabledDays){
41694             var day = value.getDay();
41695             for(var i = 0; i < this.disabledDays.length; i++) {
41696                 if(day === this.disabledDays[i]){
41697                     this.markInvalid(this.disabledDaysText);
41698                     return false;
41699                 }
41700             }
41701         }
41702         var fvalue = this.formatDate(value);
41703         if(this.ddMatch && this.ddMatch.test(fvalue)){
41704             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41705             return false;
41706         }
41707         return true;
41708     },
41709
41710     // private
41711     // Provides logic to override the default TriggerField.validateBlur which just returns true
41712     validateBlur : function(){
41713         return !this.menu || !this.menu.isVisible();
41714     },
41715     
41716     getName: function()
41717     {
41718         // returns hidden if it's set..
41719         if (!this.rendered) {return ''};
41720         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41721         
41722     },
41723
41724     /**
41725      * Returns the current date value of the date field.
41726      * @return {Date} The date value
41727      */
41728     getValue : function(){
41729         
41730         return  this.hiddenField ?
41731                 this.hiddenField.value :
41732                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
41733     },
41734
41735     /**
41736      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41737      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
41738      * (the default format used is "m/d/y").
41739      * <br />Usage:
41740      * <pre><code>
41741 //All of these calls set the same date value (May 4, 2006)
41742
41743 //Pass a date object:
41744 var dt = new Date('5/4/06');
41745 dateField.setValue(dt);
41746
41747 //Pass a date string (default format):
41748 dateField.setValue('5/4/06');
41749
41750 //Pass a date string (custom format):
41751 dateField.format = 'Y-m-d';
41752 dateField.setValue('2006-5-4');
41753 </code></pre>
41754      * @param {String/Date} date The date or valid date string
41755      */
41756     setValue : function(date){
41757         if (this.hiddenField) {
41758             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41759         }
41760         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41761         // make sure the value field is always stored as a date..
41762         this.value = this.parseDate(date);
41763         
41764         
41765     },
41766
41767     // private
41768     parseDate : function(value){
41769                 
41770                 if (value instanceof Date) {
41771                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
41772                                 return  '';
41773                         }
41774                         return value;
41775                 }
41776                 
41777                 
41778         if(!value || value instanceof Date){
41779             return value;
41780         }
41781         var v = Date.parseDate(value, this.format);
41782          if (!v && this.useIso) {
41783             v = Date.parseDate(value, 'Y-m-d');
41784         }
41785         if(!v && this.altFormats){
41786             if(!this.altFormatsArray){
41787                 this.altFormatsArray = this.altFormats.split("|");
41788             }
41789             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41790                 v = Date.parseDate(value, this.altFormatsArray[i]);
41791             }
41792         }
41793                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
41794                         v = '';
41795                 }
41796         return v;
41797     },
41798
41799     // private
41800     formatDate : function(date, fmt){
41801         return (!date || !(date instanceof Date)) ?
41802                date : date.dateFormat(fmt || this.format);
41803     },
41804
41805     // private
41806     menuListeners : {
41807         select: function(m, d){
41808             
41809             this.setValue(d);
41810             this.fireEvent('select', this, d);
41811         },
41812         show : function(){ // retain focus styling
41813             this.onFocus();
41814         },
41815         hide : function(){
41816             this.focus.defer(10, this);
41817             var ml = this.menuListeners;
41818             this.menu.un("select", ml.select,  this);
41819             this.menu.un("show", ml.show,  this);
41820             this.menu.un("hide", ml.hide,  this);
41821         }
41822     },
41823
41824     // private
41825     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41826     onTriggerClick : function(){
41827         if(this.disabled){
41828             return;
41829         }
41830         if(this.menu == null){
41831             this.menu = new Roo.menu.DateMenu();
41832         }
41833         Roo.apply(this.menu.picker,  {
41834             showClear: this.allowBlank,
41835             minDate : this.minValue,
41836             maxDate : this.maxValue,
41837             disabledDatesRE : this.ddMatch,
41838             disabledDatesText : this.disabledDatesText,
41839             disabledDays : this.disabledDays,
41840             disabledDaysText : this.disabledDaysText,
41841             format : this.useIso ? 'Y-m-d' : this.format,
41842             minText : String.format(this.minText, this.formatDate(this.minValue)),
41843             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41844         });
41845         this.menu.on(Roo.apply({}, this.menuListeners, {
41846             scope:this
41847         }));
41848         this.menu.picker.setValue(this.getValue() || new Date());
41849         this.menu.show(this.el, "tl-bl?");
41850     },
41851
41852     beforeBlur : function(){
41853         var v = this.parseDate(this.getRawValue());
41854         if(v){
41855             this.setValue(v);
41856         }
41857     },
41858
41859     /*@
41860      * overide
41861      * 
41862      */
41863     isDirty : function() {
41864         if(this.disabled) {
41865             return false;
41866         }
41867         
41868         if(typeof(this.startValue) === 'undefined'){
41869             return false;
41870         }
41871         
41872         return String(this.getValue()) !== String(this.startValue);
41873         
41874     },
41875     // @overide
41876     cleanLeadingSpace : function(e)
41877     {
41878        return;
41879     }
41880     
41881 });/*
41882  * Based on:
41883  * Ext JS Library 1.1.1
41884  * Copyright(c) 2006-2007, Ext JS, LLC.
41885  *
41886  * Originally Released Under LGPL - original licence link has changed is not relivant.
41887  *
41888  * Fork - LGPL
41889  * <script type="text/javascript">
41890  */
41891  
41892 /**
41893  * @class Roo.form.MonthField
41894  * @extends Roo.form.TriggerField
41895  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41896 * @constructor
41897 * Create a new MonthField
41898 * @param {Object} config
41899  */
41900 Roo.form.MonthField = function(config){
41901     
41902     Roo.form.MonthField.superclass.constructor.call(this, config);
41903     
41904       this.addEvents({
41905          
41906         /**
41907          * @event select
41908          * Fires when a date is selected
41909              * @param {Roo.form.MonthFieeld} combo This combo box
41910              * @param {Date} date The date selected
41911              */
41912         'select' : true
41913          
41914     });
41915     
41916     
41917     if(typeof this.minValue == "string") {
41918         this.minValue = this.parseDate(this.minValue);
41919     }
41920     if(typeof this.maxValue == "string") {
41921         this.maxValue = this.parseDate(this.maxValue);
41922     }
41923     this.ddMatch = null;
41924     if(this.disabledDates){
41925         var dd = this.disabledDates;
41926         var re = "(?:";
41927         for(var i = 0; i < dd.length; i++){
41928             re += dd[i];
41929             if(i != dd.length-1) {
41930                 re += "|";
41931             }
41932         }
41933         this.ddMatch = new RegExp(re + ")");
41934     }
41935 };
41936
41937 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41938     /**
41939      * @cfg {String} format
41940      * The default date format string which can be overriden for localization support.  The format must be
41941      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41942      */
41943     format : "M Y",
41944     /**
41945      * @cfg {String} altFormats
41946      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41947      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41948      */
41949     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41950     /**
41951      * @cfg {Array} disabledDays
41952      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41953      */
41954     disabledDays : [0,1,2,3,4,5,6],
41955     /**
41956      * @cfg {String} disabledDaysText
41957      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41958      */
41959     disabledDaysText : "Disabled",
41960     /**
41961      * @cfg {Array} disabledDates
41962      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41963      * expression so they are very powerful. Some examples:
41964      * <ul>
41965      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41966      * <li>["03/08", "09/16"] would disable those days for every year</li>
41967      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41968      * <li>["03/../2006"] would disable every day in March 2006</li>
41969      * <li>["^03"] would disable every day in every March</li>
41970      * </ul>
41971      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41972      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41973      */
41974     disabledDates : null,
41975     /**
41976      * @cfg {String} disabledDatesText
41977      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41978      */
41979     disabledDatesText : "Disabled",
41980     /**
41981      * @cfg {Date/String} minValue
41982      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41983      * valid format (defaults to null).
41984      */
41985     minValue : null,
41986     /**
41987      * @cfg {Date/String} maxValue
41988      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41989      * valid format (defaults to null).
41990      */
41991     maxValue : null,
41992     /**
41993      * @cfg {String} minText
41994      * The error text to display when the date in the cell is before minValue (defaults to
41995      * 'The date in this field must be after {minValue}').
41996      */
41997     minText : "The date in this field must be equal to or after {0}",
41998     /**
41999      * @cfg {String} maxTextf
42000      * The error text to display when the date in the cell is after maxValue (defaults to
42001      * 'The date in this field must be before {maxValue}').
42002      */
42003     maxText : "The date in this field must be equal to or before {0}",
42004     /**
42005      * @cfg {String} invalidText
42006      * The error text to display when the date in the field is invalid (defaults to
42007      * '{value} is not a valid date - it must be in the format {format}').
42008      */
42009     invalidText : "{0} is not a valid date - it must be in the format {1}",
42010     /**
42011      * @cfg {String} triggerClass
42012      * An additional CSS class used to style the trigger button.  The trigger will always get the
42013      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42014      * which displays a calendar icon).
42015      */
42016     triggerClass : 'x-form-date-trigger',
42017     
42018
42019     /**
42020      * @cfg {Boolean} useIso
42021      * if enabled, then the date field will use a hidden field to store the 
42022      * real value as iso formated date. default (true)
42023      */ 
42024     useIso : true,
42025     /**
42026      * @cfg {String/Object} autoCreate
42027      * A DomHelper element spec, or true for a default element spec (defaults to
42028      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42029      */ 
42030     // private
42031     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42032     
42033     // private
42034     hiddenField: false,
42035     
42036     hideMonthPicker : false,
42037     
42038     onRender : function(ct, position)
42039     {
42040         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42041         if (this.useIso) {
42042             this.el.dom.removeAttribute('name'); 
42043             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42044                     'before', true);
42045             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42046             // prevent input submission
42047             this.hiddenName = this.name;
42048         }
42049             
42050             
42051     },
42052     
42053     // private
42054     validateValue : function(value)
42055     {
42056         value = this.formatDate(value);
42057         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42058             return false;
42059         }
42060         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42061              return true;
42062         }
42063         var svalue = value;
42064         value = this.parseDate(value);
42065         if(!value){
42066             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42067             return false;
42068         }
42069         var time = value.getTime();
42070         if(this.minValue && time < this.minValue.getTime()){
42071             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42072             return false;
42073         }
42074         if(this.maxValue && time > this.maxValue.getTime()){
42075             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42076             return false;
42077         }
42078         /*if(this.disabledDays){
42079             var day = value.getDay();
42080             for(var i = 0; i < this.disabledDays.length; i++) {
42081                 if(day === this.disabledDays[i]){
42082                     this.markInvalid(this.disabledDaysText);
42083                     return false;
42084                 }
42085             }
42086         }
42087         */
42088         var fvalue = this.formatDate(value);
42089         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42090             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42091             return false;
42092         }
42093         */
42094         return true;
42095     },
42096
42097     // private
42098     // Provides logic to override the default TriggerField.validateBlur which just returns true
42099     validateBlur : function(){
42100         return !this.menu || !this.menu.isVisible();
42101     },
42102
42103     /**
42104      * Returns the current date value of the date field.
42105      * @return {Date} The date value
42106      */
42107     getValue : function(){
42108         
42109         
42110         
42111         return  this.hiddenField ?
42112                 this.hiddenField.value :
42113                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42114     },
42115
42116     /**
42117      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42118      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42119      * (the default format used is "m/d/y").
42120      * <br />Usage:
42121      * <pre><code>
42122 //All of these calls set the same date value (May 4, 2006)
42123
42124 //Pass a date object:
42125 var dt = new Date('5/4/06');
42126 monthField.setValue(dt);
42127
42128 //Pass a date string (default format):
42129 monthField.setValue('5/4/06');
42130
42131 //Pass a date string (custom format):
42132 monthField.format = 'Y-m-d';
42133 monthField.setValue('2006-5-4');
42134 </code></pre>
42135      * @param {String/Date} date The date or valid date string
42136      */
42137     setValue : function(date){
42138         Roo.log('month setValue' + date);
42139         // can only be first of month..
42140         
42141         var val = this.parseDate(date);
42142         
42143         if (this.hiddenField) {
42144             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42145         }
42146         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42147         this.value = this.parseDate(date);
42148     },
42149
42150     // private
42151     parseDate : function(value){
42152         if(!value || value instanceof Date){
42153             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42154             return value;
42155         }
42156         var v = Date.parseDate(value, this.format);
42157         if (!v && this.useIso) {
42158             v = Date.parseDate(value, 'Y-m-d');
42159         }
42160         if (v) {
42161             // 
42162             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42163         }
42164         
42165         
42166         if(!v && this.altFormats){
42167             if(!this.altFormatsArray){
42168                 this.altFormatsArray = this.altFormats.split("|");
42169             }
42170             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42171                 v = Date.parseDate(value, this.altFormatsArray[i]);
42172             }
42173         }
42174         return v;
42175     },
42176
42177     // private
42178     formatDate : function(date, fmt){
42179         return (!date || !(date instanceof Date)) ?
42180                date : date.dateFormat(fmt || this.format);
42181     },
42182
42183     // private
42184     menuListeners : {
42185         select: function(m, d){
42186             this.setValue(d);
42187             this.fireEvent('select', this, d);
42188         },
42189         show : function(){ // retain focus styling
42190             this.onFocus();
42191         },
42192         hide : function(){
42193             this.focus.defer(10, this);
42194             var ml = this.menuListeners;
42195             this.menu.un("select", ml.select,  this);
42196             this.menu.un("show", ml.show,  this);
42197             this.menu.un("hide", ml.hide,  this);
42198         }
42199     },
42200     // private
42201     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42202     onTriggerClick : function(){
42203         if(this.disabled){
42204             return;
42205         }
42206         if(this.menu == null){
42207             this.menu = new Roo.menu.DateMenu();
42208            
42209         }
42210         
42211         Roo.apply(this.menu.picker,  {
42212             
42213             showClear: this.allowBlank,
42214             minDate : this.minValue,
42215             maxDate : this.maxValue,
42216             disabledDatesRE : this.ddMatch,
42217             disabledDatesText : this.disabledDatesText,
42218             
42219             format : this.useIso ? 'Y-m-d' : this.format,
42220             minText : String.format(this.minText, this.formatDate(this.minValue)),
42221             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42222             
42223         });
42224          this.menu.on(Roo.apply({}, this.menuListeners, {
42225             scope:this
42226         }));
42227        
42228         
42229         var m = this.menu;
42230         var p = m.picker;
42231         
42232         // hide month picker get's called when we called by 'before hide';
42233         
42234         var ignorehide = true;
42235         p.hideMonthPicker  = function(disableAnim){
42236             if (ignorehide) {
42237                 return;
42238             }
42239              if(this.monthPicker){
42240                 Roo.log("hideMonthPicker called");
42241                 if(disableAnim === true){
42242                     this.monthPicker.hide();
42243                 }else{
42244                     this.monthPicker.slideOut('t', {duration:.2});
42245                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42246                     p.fireEvent("select", this, this.value);
42247                     m.hide();
42248                 }
42249             }
42250         }
42251         
42252         Roo.log('picker set value');
42253         Roo.log(this.getValue());
42254         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42255         m.show(this.el, 'tl-bl?');
42256         ignorehide  = false;
42257         // this will trigger hideMonthPicker..
42258         
42259         
42260         // hidden the day picker
42261         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42262         
42263         
42264         
42265       
42266         
42267         p.showMonthPicker.defer(100, p);
42268     
42269         
42270        
42271     },
42272
42273     beforeBlur : function(){
42274         var v = this.parseDate(this.getRawValue());
42275         if(v){
42276             this.setValue(v);
42277         }
42278     }
42279
42280     /** @cfg {Boolean} grow @hide */
42281     /** @cfg {Number} growMin @hide */
42282     /** @cfg {Number} growMax @hide */
42283     /**
42284      * @hide
42285      * @method autoSize
42286      */
42287 });/*
42288  * Based on:
42289  * Ext JS Library 1.1.1
42290  * Copyright(c) 2006-2007, Ext JS, LLC.
42291  *
42292  * Originally Released Under LGPL - original licence link has changed is not relivant.
42293  *
42294  * Fork - LGPL
42295  * <script type="text/javascript">
42296  */
42297  
42298
42299 /**
42300  * @class Roo.form.ComboBox
42301  * @extends Roo.form.TriggerField
42302  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42303  * @constructor
42304  * Create a new ComboBox.
42305  * @param {Object} config Configuration options
42306  */
42307 Roo.form.ComboBox = function(config){
42308     Roo.form.ComboBox.superclass.constructor.call(this, config);
42309     this.addEvents({
42310         /**
42311          * @event expand
42312          * Fires when the dropdown list is expanded
42313              * @param {Roo.form.ComboBox} combo This combo box
42314              */
42315         'expand' : true,
42316         /**
42317          * @event collapse
42318          * Fires when the dropdown list is collapsed
42319              * @param {Roo.form.ComboBox} combo This combo box
42320              */
42321         'collapse' : true,
42322         /**
42323          * @event beforeselect
42324          * Fires before a list item is selected. Return false to cancel the selection.
42325              * @param {Roo.form.ComboBox} combo This combo box
42326              * @param {Roo.data.Record} record The data record returned from the underlying store
42327              * @param {Number} index The index of the selected item in the dropdown list
42328              */
42329         'beforeselect' : true,
42330         /**
42331          * @event select
42332          * Fires when a list item is selected
42333              * @param {Roo.form.ComboBox} combo This combo box
42334              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42335              * @param {Number} index The index of the selected item in the dropdown list
42336              */
42337         'select' : true,
42338         /**
42339          * @event beforequery
42340          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42341          * The event object passed has these properties:
42342              * @param {Roo.form.ComboBox} combo This combo box
42343              * @param {String} query The query
42344              * @param {Boolean} forceAll true to force "all" query
42345              * @param {Boolean} cancel true to cancel the query
42346              * @param {Object} e The query event object
42347              */
42348         'beforequery': true,
42349          /**
42350          * @event add
42351          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42352              * @param {Roo.form.ComboBox} combo This combo box
42353              */
42354         'add' : true,
42355         /**
42356          * @event edit
42357          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42358              * @param {Roo.form.ComboBox} combo This combo box
42359              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42360              */
42361         'edit' : true
42362         
42363         
42364     });
42365     if(this.transform){
42366         this.allowDomMove = false;
42367         var s = Roo.getDom(this.transform);
42368         if(!this.hiddenName){
42369             this.hiddenName = s.name;
42370         }
42371         if(!this.store){
42372             this.mode = 'local';
42373             var d = [], opts = s.options;
42374             for(var i = 0, len = opts.length;i < len; i++){
42375                 var o = opts[i];
42376                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42377                 if(o.selected) {
42378                     this.value = value;
42379                 }
42380                 d.push([value, o.text]);
42381             }
42382             this.store = new Roo.data.SimpleStore({
42383                 'id': 0,
42384                 fields: ['value', 'text'],
42385                 data : d
42386             });
42387             this.valueField = 'value';
42388             this.displayField = 'text';
42389         }
42390         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42391         if(!this.lazyRender){
42392             this.target = true;
42393             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42394             s.parentNode.removeChild(s); // remove it
42395             this.render(this.el.parentNode);
42396         }else{
42397             s.parentNode.removeChild(s); // remove it
42398         }
42399
42400     }
42401     if (this.store) {
42402         this.store = Roo.factory(this.store, Roo.data);
42403     }
42404     
42405     this.selectedIndex = -1;
42406     if(this.mode == 'local'){
42407         if(config.queryDelay === undefined){
42408             this.queryDelay = 10;
42409         }
42410         if(config.minChars === undefined){
42411             this.minChars = 0;
42412         }
42413     }
42414 };
42415
42416 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42417     /**
42418      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42419      */
42420     /**
42421      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42422      * rendering into an Roo.Editor, defaults to false)
42423      */
42424     /**
42425      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42426      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42427      */
42428     /**
42429      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42430      */
42431     /**
42432      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42433      * the dropdown list (defaults to undefined, with no header element)
42434      */
42435
42436      /**
42437      * @cfg {String/Roo.Template} tpl The template to use to render the output
42438      */
42439      
42440     // private
42441     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42442     /**
42443      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42444      */
42445     listWidth: undefined,
42446     /**
42447      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42448      * mode = 'remote' or 'text' if mode = 'local')
42449      */
42450     displayField: undefined,
42451     /**
42452      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42453      * mode = 'remote' or 'value' if mode = 'local'). 
42454      * Note: use of a valueField requires the user make a selection
42455      * in order for a value to be mapped.
42456      */
42457     valueField: undefined,
42458     
42459     
42460     /**
42461      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42462      * field's data value (defaults to the underlying DOM element's name)
42463      */
42464     hiddenName: undefined,
42465     /**
42466      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42467      */
42468     listClass: '',
42469     /**
42470      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42471      */
42472     selectedClass: 'x-combo-selected',
42473     /**
42474      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42475      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42476      * which displays a downward arrow icon).
42477      */
42478     triggerClass : 'x-form-arrow-trigger',
42479     /**
42480      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42481      */
42482     shadow:'sides',
42483     /**
42484      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42485      * anchor positions (defaults to 'tl-bl')
42486      */
42487     listAlign: 'tl-bl?',
42488     /**
42489      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42490      */
42491     maxHeight: 300,
42492     /**
42493      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42494      * query specified by the allQuery config option (defaults to 'query')
42495      */
42496     triggerAction: 'query',
42497     /**
42498      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42499      * (defaults to 4, does not apply if editable = false)
42500      */
42501     minChars : 4,
42502     /**
42503      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42504      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42505      */
42506     typeAhead: false,
42507     /**
42508      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42509      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42510      */
42511     queryDelay: 500,
42512     /**
42513      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42514      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42515      */
42516     pageSize: 0,
42517     /**
42518      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42519      * when editable = true (defaults to false)
42520      */
42521     selectOnFocus:false,
42522     /**
42523      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42524      */
42525     queryParam: 'query',
42526     /**
42527      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42528      * when mode = 'remote' (defaults to 'Loading...')
42529      */
42530     loadingText: 'Loading...',
42531     /**
42532      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42533      */
42534     resizable: false,
42535     /**
42536      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42537      */
42538     handleHeight : 8,
42539     /**
42540      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42541      * traditional select (defaults to true)
42542      */
42543     editable: true,
42544     /**
42545      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42546      */
42547     allQuery: '',
42548     /**
42549      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42550      */
42551     mode: 'remote',
42552     /**
42553      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42554      * listWidth has a higher value)
42555      */
42556     minListWidth : 70,
42557     /**
42558      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42559      * allow the user to set arbitrary text into the field (defaults to false)
42560      */
42561     forceSelection:false,
42562     /**
42563      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42564      * if typeAhead = true (defaults to 250)
42565      */
42566     typeAheadDelay : 250,
42567     /**
42568      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42569      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42570      */
42571     valueNotFoundText : undefined,
42572     /**
42573      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42574      */
42575     blockFocus : false,
42576     
42577     /**
42578      * @cfg {Boolean} disableClear Disable showing of clear button.
42579      */
42580     disableClear : false,
42581     /**
42582      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42583      */
42584     alwaysQuery : false,
42585     
42586     //private
42587     addicon : false,
42588     editicon: false,
42589     
42590     // element that contains real text value.. (when hidden is used..)
42591      
42592     // private
42593     onRender : function(ct, position)
42594     {
42595         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42596         
42597         if(this.hiddenName){
42598             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42599                     'before', true);
42600             this.hiddenField.value =
42601                 this.hiddenValue !== undefined ? this.hiddenValue :
42602                 this.value !== undefined ? this.value : '';
42603
42604             // prevent input submission
42605             this.el.dom.removeAttribute('name');
42606              
42607              
42608         }
42609         
42610         if(Roo.isGecko){
42611             this.el.dom.setAttribute('autocomplete', 'off');
42612         }
42613
42614         var cls = 'x-combo-list';
42615
42616         this.list = new Roo.Layer({
42617             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42618         });
42619
42620         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42621         this.list.setWidth(lw);
42622         this.list.swallowEvent('mousewheel');
42623         this.assetHeight = 0;
42624
42625         if(this.title){
42626             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42627             this.assetHeight += this.header.getHeight();
42628         }
42629
42630         this.innerList = this.list.createChild({cls:cls+'-inner'});
42631         this.innerList.on('mouseover', this.onViewOver, this);
42632         this.innerList.on('mousemove', this.onViewMove, this);
42633         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42634         
42635         if(this.allowBlank && !this.pageSize && !this.disableClear){
42636             this.footer = this.list.createChild({cls:cls+'-ft'});
42637             this.pageTb = new Roo.Toolbar(this.footer);
42638            
42639         }
42640         if(this.pageSize){
42641             this.footer = this.list.createChild({cls:cls+'-ft'});
42642             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42643                     {pageSize: this.pageSize});
42644             
42645         }
42646         
42647         if (this.pageTb && this.allowBlank && !this.disableClear) {
42648             var _this = this;
42649             this.pageTb.add(new Roo.Toolbar.Fill(), {
42650                 cls: 'x-btn-icon x-btn-clear',
42651                 text: '&#160;',
42652                 handler: function()
42653                 {
42654                     _this.collapse();
42655                     _this.clearValue();
42656                     _this.onSelect(false, -1);
42657                 }
42658             });
42659         }
42660         if (this.footer) {
42661             this.assetHeight += this.footer.getHeight();
42662         }
42663         
42664
42665         if(!this.tpl){
42666             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42667         }
42668
42669         this.view = new Roo.View(this.innerList, this.tpl, {
42670             singleSelect:true,
42671             store: this.store,
42672             selectedClass: this.selectedClass
42673         });
42674
42675         this.view.on('click', this.onViewClick, this);
42676
42677         this.store.on('beforeload', this.onBeforeLoad, this);
42678         this.store.on('load', this.onLoad, this);
42679         this.store.on('loadexception', this.onLoadException, this);
42680
42681         if(this.resizable){
42682             this.resizer = new Roo.Resizable(this.list,  {
42683                pinned:true, handles:'se'
42684             });
42685             this.resizer.on('resize', function(r, w, h){
42686                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42687                 this.listWidth = w;
42688                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42689                 this.restrictHeight();
42690             }, this);
42691             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42692         }
42693         if(!this.editable){
42694             this.editable = true;
42695             this.setEditable(false);
42696         }  
42697         
42698         
42699         if (typeof(this.events.add.listeners) != 'undefined') {
42700             
42701             this.addicon = this.wrap.createChild(
42702                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
42703        
42704             this.addicon.on('click', function(e) {
42705                 this.fireEvent('add', this);
42706             }, this);
42707         }
42708         if (typeof(this.events.edit.listeners) != 'undefined') {
42709             
42710             this.editicon = this.wrap.createChild(
42711                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
42712             if (this.addicon) {
42713                 this.editicon.setStyle('margin-left', '40px');
42714             }
42715             this.editicon.on('click', function(e) {
42716                 
42717                 // we fire even  if inothing is selected..
42718                 this.fireEvent('edit', this, this.lastData );
42719                 
42720             }, this);
42721         }
42722         
42723         
42724         
42725     },
42726
42727     // private
42728     initEvents : function(){
42729         Roo.form.ComboBox.superclass.initEvents.call(this);
42730
42731         this.keyNav = new Roo.KeyNav(this.el, {
42732             "up" : function(e){
42733                 this.inKeyMode = true;
42734                 this.selectPrev();
42735             },
42736
42737             "down" : function(e){
42738                 if(!this.isExpanded()){
42739                     this.onTriggerClick();
42740                 }else{
42741                     this.inKeyMode = true;
42742                     this.selectNext();
42743                 }
42744             },
42745
42746             "enter" : function(e){
42747                 this.onViewClick();
42748                 //return true;
42749             },
42750
42751             "esc" : function(e){
42752                 this.collapse();
42753             },
42754
42755             "tab" : function(e){
42756                 this.onViewClick(false);
42757                 this.fireEvent("specialkey", this, e);
42758                 return true;
42759             },
42760
42761             scope : this,
42762
42763             doRelay : function(foo, bar, hname){
42764                 if(hname == 'down' || this.scope.isExpanded()){
42765                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42766                 }
42767                 return true;
42768             },
42769
42770             forceKeyDown: true
42771         });
42772         this.queryDelay = Math.max(this.queryDelay || 10,
42773                 this.mode == 'local' ? 10 : 250);
42774         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
42775         if(this.typeAhead){
42776             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
42777         }
42778         if(this.editable !== false){
42779             this.el.on("keyup", this.onKeyUp, this);
42780         }
42781         if(this.forceSelection){
42782             this.on('blur', this.doForce, this);
42783         }
42784     },
42785
42786     onDestroy : function(){
42787         if(this.view){
42788             this.view.setStore(null);
42789             this.view.el.removeAllListeners();
42790             this.view.el.remove();
42791             this.view.purgeListeners();
42792         }
42793         if(this.list){
42794             this.list.destroy();
42795         }
42796         if(this.store){
42797             this.store.un('beforeload', this.onBeforeLoad, this);
42798             this.store.un('load', this.onLoad, this);
42799             this.store.un('loadexception', this.onLoadException, this);
42800         }
42801         Roo.form.ComboBox.superclass.onDestroy.call(this);
42802     },
42803
42804     // private
42805     fireKey : function(e){
42806         if(e.isNavKeyPress() && !this.list.isVisible()){
42807             this.fireEvent("specialkey", this, e);
42808         }
42809     },
42810
42811     // private
42812     onResize: function(w, h){
42813         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
42814         
42815         if(typeof w != 'number'){
42816             // we do not handle it!?!?
42817             return;
42818         }
42819         var tw = this.trigger.getWidth();
42820         tw += this.addicon ? this.addicon.getWidth() : 0;
42821         tw += this.editicon ? this.editicon.getWidth() : 0;
42822         var x = w - tw;
42823         this.el.setWidth( this.adjustWidth('input', x));
42824             
42825         this.trigger.setStyle('left', x+'px');
42826         
42827         if(this.list && this.listWidth === undefined){
42828             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
42829             this.list.setWidth(lw);
42830             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42831         }
42832         
42833     
42834         
42835     },
42836
42837     /**
42838      * Allow or prevent the user from directly editing the field text.  If false is passed,
42839      * the user will only be able to select from the items defined in the dropdown list.  This method
42840      * is the runtime equivalent of setting the 'editable' config option at config time.
42841      * @param {Boolean} value True to allow the user to directly edit the field text
42842      */
42843     setEditable : function(value){
42844         if(value == this.editable){
42845             return;
42846         }
42847         this.editable = value;
42848         if(!value){
42849             this.el.dom.setAttribute('readOnly', true);
42850             this.el.on('mousedown', this.onTriggerClick,  this);
42851             this.el.addClass('x-combo-noedit');
42852         }else{
42853             this.el.dom.setAttribute('readOnly', false);
42854             this.el.un('mousedown', this.onTriggerClick,  this);
42855             this.el.removeClass('x-combo-noedit');
42856         }
42857     },
42858
42859     // private
42860     onBeforeLoad : function(){
42861         if(!this.hasFocus){
42862             return;
42863         }
42864         this.innerList.update(this.loadingText ?
42865                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
42866         this.restrictHeight();
42867         this.selectedIndex = -1;
42868     },
42869
42870     // private
42871     onLoad : function(){
42872         if(!this.hasFocus){
42873             return;
42874         }
42875         if(this.store.getCount() > 0){
42876             this.expand();
42877             this.restrictHeight();
42878             if(this.lastQuery == this.allQuery){
42879                 if(this.editable){
42880                     this.el.dom.select();
42881                 }
42882                 if(!this.selectByValue(this.value, true)){
42883                     this.select(0, true);
42884                 }
42885             }else{
42886                 this.selectNext();
42887                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
42888                     this.taTask.delay(this.typeAheadDelay);
42889                 }
42890             }
42891         }else{
42892             this.onEmptyResults();
42893         }
42894         //this.el.focus();
42895     },
42896     // private
42897     onLoadException : function()
42898     {
42899         this.collapse();
42900         Roo.log(this.store.reader.jsonData);
42901         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42902             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42903         }
42904         
42905         
42906     },
42907     // private
42908     onTypeAhead : function(){
42909         if(this.store.getCount() > 0){
42910             var r = this.store.getAt(0);
42911             var newValue = r.data[this.displayField];
42912             var len = newValue.length;
42913             var selStart = this.getRawValue().length;
42914             if(selStart != len){
42915                 this.setRawValue(newValue);
42916                 this.selectText(selStart, newValue.length);
42917             }
42918         }
42919     },
42920
42921     // private
42922     onSelect : function(record, index){
42923         if(this.fireEvent('beforeselect', this, record, index) !== false){
42924             this.setFromData(index > -1 ? record.data : false);
42925             this.collapse();
42926             this.fireEvent('select', this, record, index);
42927         }
42928     },
42929
42930     /**
42931      * Returns the currently selected field value or empty string if no value is set.
42932      * @return {String} value The selected value
42933      */
42934     getValue : function(){
42935         if(this.valueField){
42936             return typeof this.value != 'undefined' ? this.value : '';
42937         }
42938         return Roo.form.ComboBox.superclass.getValue.call(this);
42939     },
42940
42941     /**
42942      * Clears any text/value currently set in the field
42943      */
42944     clearValue : function(){
42945         if(this.hiddenField){
42946             this.hiddenField.value = '';
42947         }
42948         this.value = '';
42949         this.setRawValue('');
42950         this.lastSelectionText = '';
42951         
42952     },
42953
42954     /**
42955      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42956      * will be displayed in the field.  If the value does not match the data value of an existing item,
42957      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42958      * Otherwise the field will be blank (although the value will still be set).
42959      * @param {String} value The value to match
42960      */
42961     setValue : function(v){
42962         var text = v;
42963         if(this.valueField){
42964             var r = this.findRecord(this.valueField, v);
42965             if(r){
42966                 text = r.data[this.displayField];
42967             }else if(this.valueNotFoundText !== undefined){
42968                 text = this.valueNotFoundText;
42969             }
42970         }
42971         this.lastSelectionText = text;
42972         if(this.hiddenField){
42973             this.hiddenField.value = v;
42974         }
42975         Roo.form.ComboBox.superclass.setValue.call(this, text);
42976         this.value = v;
42977     },
42978     /**
42979      * @property {Object} the last set data for the element
42980      */
42981     
42982     lastData : false,
42983     /**
42984      * Sets the value of the field based on a object which is related to the record format for the store.
42985      * @param {Object} value the value to set as. or false on reset?
42986      */
42987     setFromData : function(o){
42988         var dv = ''; // display value
42989         var vv = ''; // value value..
42990         this.lastData = o;
42991         if (this.displayField) {
42992             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42993         } else {
42994             // this is an error condition!!!
42995             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42996         }
42997         
42998         if(this.valueField){
42999             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
43000         }
43001         if(this.hiddenField){
43002             this.hiddenField.value = vv;
43003             
43004             this.lastSelectionText = dv;
43005             Roo.form.ComboBox.superclass.setValue.call(this, dv);
43006             this.value = vv;
43007             return;
43008         }
43009         // no hidden field.. - we store the value in 'value', but still display
43010         // display field!!!!
43011         this.lastSelectionText = dv;
43012         Roo.form.ComboBox.superclass.setValue.call(this, dv);
43013         this.value = vv;
43014         
43015         
43016     },
43017     // private
43018     reset : function(){
43019         // overridden so that last data is reset..
43020         this.setValue(this.resetValue);
43021         this.originalValue = this.getValue();
43022         this.clearInvalid();
43023         this.lastData = false;
43024         if (this.view) {
43025             this.view.clearSelections();
43026         }
43027     },
43028     // private
43029     findRecord : function(prop, value){
43030         var record;
43031         if(this.store.getCount() > 0){
43032             this.store.each(function(r){
43033                 if(r.data[prop] == value){
43034                     record = r;
43035                     return false;
43036                 }
43037                 return true;
43038             });
43039         }
43040         return record;
43041     },
43042     
43043     getName: function()
43044     {
43045         // returns hidden if it's set..
43046         if (!this.rendered) {return ''};
43047         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
43048         
43049     },
43050     // private
43051     onViewMove : function(e, t){
43052         this.inKeyMode = false;
43053     },
43054
43055     // private
43056     onViewOver : function(e, t){
43057         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43058             return;
43059         }
43060         var item = this.view.findItemFromChild(t);
43061         if(item){
43062             var index = this.view.indexOf(item);
43063             this.select(index, false);
43064         }
43065     },
43066
43067     // private
43068     onViewClick : function(doFocus)
43069     {
43070         var index = this.view.getSelectedIndexes()[0];
43071         var r = this.store.getAt(index);
43072         if(r){
43073             this.onSelect(r, index);
43074         }
43075         if(doFocus !== false && !this.blockFocus){
43076             this.el.focus();
43077         }
43078     },
43079
43080     // private
43081     restrictHeight : function(){
43082         this.innerList.dom.style.height = '';
43083         var inner = this.innerList.dom;
43084         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43085         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43086         this.list.beginUpdate();
43087         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43088         this.list.alignTo(this.el, this.listAlign);
43089         this.list.endUpdate();
43090     },
43091
43092     // private
43093     onEmptyResults : function(){
43094         this.collapse();
43095     },
43096
43097     /**
43098      * Returns true if the dropdown list is expanded, else false.
43099      */
43100     isExpanded : function(){
43101         return this.list.isVisible();
43102     },
43103
43104     /**
43105      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43106      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43107      * @param {String} value The data value of the item to select
43108      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43109      * selected item if it is not currently in view (defaults to true)
43110      * @return {Boolean} True if the value matched an item in the list, else false
43111      */
43112     selectByValue : function(v, scrollIntoView){
43113         if(v !== undefined && v !== null){
43114             var r = this.findRecord(this.valueField || this.displayField, v);
43115             if(r){
43116                 this.select(this.store.indexOf(r), scrollIntoView);
43117                 return true;
43118             }
43119         }
43120         return false;
43121     },
43122
43123     /**
43124      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43125      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43126      * @param {Number} index The zero-based index of the list item to select
43127      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43128      * selected item if it is not currently in view (defaults to true)
43129      */
43130     select : function(index, scrollIntoView){
43131         this.selectedIndex = index;
43132         this.view.select(index);
43133         if(scrollIntoView !== false){
43134             var el = this.view.getNode(index);
43135             if(el){
43136                 this.innerList.scrollChildIntoView(el, false);
43137             }
43138         }
43139     },
43140
43141     // private
43142     selectNext : function(){
43143         var ct = this.store.getCount();
43144         if(ct > 0){
43145             if(this.selectedIndex == -1){
43146                 this.select(0);
43147             }else if(this.selectedIndex < ct-1){
43148                 this.select(this.selectedIndex+1);
43149             }
43150         }
43151     },
43152
43153     // private
43154     selectPrev : function(){
43155         var ct = this.store.getCount();
43156         if(ct > 0){
43157             if(this.selectedIndex == -1){
43158                 this.select(0);
43159             }else if(this.selectedIndex != 0){
43160                 this.select(this.selectedIndex-1);
43161             }
43162         }
43163     },
43164
43165     // private
43166     onKeyUp : function(e){
43167         if(this.editable !== false && !e.isSpecialKey()){
43168             this.lastKey = e.getKey();
43169             this.dqTask.delay(this.queryDelay);
43170         }
43171     },
43172
43173     // private
43174     validateBlur : function(){
43175         return !this.list || !this.list.isVisible();   
43176     },
43177
43178     // private
43179     initQuery : function(){
43180         this.doQuery(this.getRawValue());
43181     },
43182
43183     // private
43184     doForce : function(){
43185         if(this.el.dom.value.length > 0){
43186             this.el.dom.value =
43187                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43188              
43189         }
43190     },
43191
43192     /**
43193      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43194      * query allowing the query action to be canceled if needed.
43195      * @param {String} query The SQL query to execute
43196      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43197      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43198      * saved in the current store (defaults to false)
43199      */
43200     doQuery : function(q, forceAll){
43201         if(q === undefined || q === null){
43202             q = '';
43203         }
43204         var qe = {
43205             query: q,
43206             forceAll: forceAll,
43207             combo: this,
43208             cancel:false
43209         };
43210         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43211             return false;
43212         }
43213         q = qe.query;
43214         forceAll = qe.forceAll;
43215         if(forceAll === true || (q.length >= this.minChars)){
43216             if(this.lastQuery != q || this.alwaysQuery){
43217                 this.lastQuery = q;
43218                 if(this.mode == 'local'){
43219                     this.selectedIndex = -1;
43220                     if(forceAll){
43221                         this.store.clearFilter();
43222                     }else{
43223                         this.store.filter(this.displayField, q);
43224                     }
43225                     this.onLoad();
43226                 }else{
43227                     this.store.baseParams[this.queryParam] = q;
43228                     this.store.load({
43229                         params: this.getParams(q)
43230                     });
43231                     this.expand();
43232                 }
43233             }else{
43234                 this.selectedIndex = -1;
43235                 this.onLoad();   
43236             }
43237         }
43238     },
43239
43240     // private
43241     getParams : function(q){
43242         var p = {};
43243         //p[this.queryParam] = q;
43244         if(this.pageSize){
43245             p.start = 0;
43246             p.limit = this.pageSize;
43247         }
43248         return p;
43249     },
43250
43251     /**
43252      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43253      */
43254     collapse : function(){
43255         if(!this.isExpanded()){
43256             return;
43257         }
43258         this.list.hide();
43259         Roo.get(document).un('mousedown', this.collapseIf, this);
43260         Roo.get(document).un('mousewheel', this.collapseIf, this);
43261         if (!this.editable) {
43262             Roo.get(document).un('keydown', this.listKeyPress, this);
43263         }
43264         this.fireEvent('collapse', this);
43265     },
43266
43267     // private
43268     collapseIf : function(e){
43269         if(!e.within(this.wrap) && !e.within(this.list)){
43270             this.collapse();
43271         }
43272     },
43273
43274     /**
43275      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43276      */
43277     expand : function(){
43278         if(this.isExpanded() || !this.hasFocus){
43279             return;
43280         }
43281         this.list.alignTo(this.el, this.listAlign);
43282         this.list.show();
43283         Roo.get(document).on('mousedown', this.collapseIf, this);
43284         Roo.get(document).on('mousewheel', this.collapseIf, this);
43285         if (!this.editable) {
43286             Roo.get(document).on('keydown', this.listKeyPress, this);
43287         }
43288         
43289         this.fireEvent('expand', this);
43290     },
43291
43292     // private
43293     // Implements the default empty TriggerField.onTriggerClick function
43294     onTriggerClick : function(){
43295         if(this.disabled){
43296             return;
43297         }
43298         if(this.isExpanded()){
43299             this.collapse();
43300             if (!this.blockFocus) {
43301                 this.el.focus();
43302             }
43303             
43304         }else {
43305             this.hasFocus = true;
43306             if(this.triggerAction == 'all') {
43307                 this.doQuery(this.allQuery, true);
43308             } else {
43309                 this.doQuery(this.getRawValue());
43310             }
43311             if (!this.blockFocus) {
43312                 this.el.focus();
43313             }
43314         }
43315     },
43316     listKeyPress : function(e)
43317     {
43318         //Roo.log('listkeypress');
43319         // scroll to first matching element based on key pres..
43320         if (e.isSpecialKey()) {
43321             return false;
43322         }
43323         var k = String.fromCharCode(e.getKey()).toUpperCase();
43324         //Roo.log(k);
43325         var match  = false;
43326         var csel = this.view.getSelectedNodes();
43327         var cselitem = false;
43328         if (csel.length) {
43329             var ix = this.view.indexOf(csel[0]);
43330             cselitem  = this.store.getAt(ix);
43331             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43332                 cselitem = false;
43333             }
43334             
43335         }
43336         
43337         this.store.each(function(v) { 
43338             if (cselitem) {
43339                 // start at existing selection.
43340                 if (cselitem.id == v.id) {
43341                     cselitem = false;
43342                 }
43343                 return;
43344             }
43345                 
43346             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43347                 match = this.store.indexOf(v);
43348                 return false;
43349             }
43350         }, this);
43351         
43352         if (match === false) {
43353             return true; // no more action?
43354         }
43355         // scroll to?
43356         this.view.select(match);
43357         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43358         sn.scrollIntoView(sn.dom.parentNode, false);
43359     } 
43360
43361     /** 
43362     * @cfg {Boolean} grow 
43363     * @hide 
43364     */
43365     /** 
43366     * @cfg {Number} growMin 
43367     * @hide 
43368     */
43369     /** 
43370     * @cfg {Number} growMax 
43371     * @hide 
43372     */
43373     /**
43374      * @hide
43375      * @method autoSize
43376      */
43377 });/*
43378  * Copyright(c) 2010-2012, Roo J Solutions Limited
43379  *
43380  * Licence LGPL
43381  *
43382  */
43383
43384 /**
43385  * @class Roo.form.ComboBoxArray
43386  * @extends Roo.form.TextField
43387  * A facebook style adder... for lists of email / people / countries  etc...
43388  * pick multiple items from a combo box, and shows each one.
43389  *
43390  *  Fred [x]  Brian [x]  [Pick another |v]
43391  *
43392  *
43393  *  For this to work: it needs various extra information
43394  *    - normal combo problay has
43395  *      name, hiddenName
43396  *    + displayField, valueField
43397  *
43398  *    For our purpose...
43399  *
43400  *
43401  *   If we change from 'extends' to wrapping...
43402  *   
43403  *  
43404  *
43405  
43406  
43407  * @constructor
43408  * Create a new ComboBoxArray.
43409  * @param {Object} config Configuration options
43410  */
43411  
43412
43413 Roo.form.ComboBoxArray = function(config)
43414 {
43415     this.addEvents({
43416         /**
43417          * @event beforeremove
43418          * Fires before remove the value from the list
43419              * @param {Roo.form.ComboBoxArray} _self This combo box array
43420              * @param {Roo.form.ComboBoxArray.Item} item removed item
43421              */
43422         'beforeremove' : true,
43423         /**
43424          * @event remove
43425          * Fires when remove the value from the list
43426              * @param {Roo.form.ComboBoxArray} _self This combo box array
43427              * @param {Roo.form.ComboBoxArray.Item} item removed item
43428              */
43429         'remove' : true
43430         
43431         
43432     });
43433     
43434     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43435     
43436     this.items = new Roo.util.MixedCollection(false);
43437     
43438     // construct the child combo...
43439     
43440     
43441     
43442     
43443    
43444     
43445 }
43446
43447  
43448 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43449
43450     /**
43451      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43452      */
43453     
43454     lastData : false,
43455     
43456     // behavies liek a hiddne field
43457     inputType:      'hidden',
43458     /**
43459      * @cfg {Number} width The width of the box that displays the selected element
43460      */ 
43461     width:          300,
43462
43463     
43464     
43465     /**
43466      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43467      */
43468     name : false,
43469     /**
43470      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43471      */
43472     hiddenName : false,
43473       /**
43474      * @cfg {String} seperator    The value seperator normally ',' 
43475      */
43476     seperator : ',',
43477     
43478     // private the array of items that are displayed..
43479     items  : false,
43480     // private - the hidden field el.
43481     hiddenEl : false,
43482     // private - the filed el..
43483     el : false,
43484     
43485     //validateValue : function() { return true; }, // all values are ok!
43486     //onAddClick: function() { },
43487     
43488     onRender : function(ct, position) 
43489     {
43490         
43491         // create the standard hidden element
43492         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43493         
43494         
43495         // give fake names to child combo;
43496         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43497         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43498         
43499         this.combo = Roo.factory(this.combo, Roo.form);
43500         this.combo.onRender(ct, position);
43501         if (typeof(this.combo.width) != 'undefined') {
43502             this.combo.onResize(this.combo.width,0);
43503         }
43504         
43505         this.combo.initEvents();
43506         
43507         // assigned so form know we need to do this..
43508         this.store          = this.combo.store;
43509         this.valueField     = this.combo.valueField;
43510         this.displayField   = this.combo.displayField ;
43511         
43512         
43513         this.combo.wrap.addClass('x-cbarray-grp');
43514         
43515         var cbwrap = this.combo.wrap.createChild(
43516             {tag: 'div', cls: 'x-cbarray-cb'},
43517             this.combo.el.dom
43518         );
43519         
43520              
43521         this.hiddenEl = this.combo.wrap.createChild({
43522             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43523         });
43524         this.el = this.combo.wrap.createChild({
43525             tag: 'input',  type:'hidden' , name: this.name, value : ''
43526         });
43527          //   this.el.dom.removeAttribute("name");
43528         
43529         
43530         this.outerWrap = this.combo.wrap;
43531         this.wrap = cbwrap;
43532         
43533         this.outerWrap.setWidth(this.width);
43534         this.outerWrap.dom.removeChild(this.el.dom);
43535         
43536         this.wrap.dom.appendChild(this.el.dom);
43537         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43538         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43539         
43540         this.combo.trigger.setStyle('position','relative');
43541         this.combo.trigger.setStyle('left', '0px');
43542         this.combo.trigger.setStyle('top', '2px');
43543         
43544         this.combo.el.setStyle('vertical-align', 'text-bottom');
43545         
43546         //this.trigger.setStyle('vertical-align', 'top');
43547         
43548         // this should use the code from combo really... on('add' ....)
43549         if (this.adder) {
43550             
43551         
43552             this.adder = this.outerWrap.createChild(
43553                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43554             var _t = this;
43555             this.adder.on('click', function(e) {
43556                 _t.fireEvent('adderclick', this, e);
43557             }, _t);
43558         }
43559         //var _t = this;
43560         //this.adder.on('click', this.onAddClick, _t);
43561         
43562         
43563         this.combo.on('select', function(cb, rec, ix) {
43564             this.addItem(rec.data);
43565             
43566             cb.setValue('');
43567             cb.el.dom.value = '';
43568             //cb.lastData = rec.data;
43569             // add to list
43570             
43571         }, this);
43572         
43573         
43574     },
43575     
43576     
43577     getName: function()
43578     {
43579         // returns hidden if it's set..
43580         if (!this.rendered) {return ''};
43581         return  this.hiddenName ? this.hiddenName : this.name;
43582         
43583     },
43584     
43585     
43586     onResize: function(w, h){
43587         
43588         return;
43589         // not sure if this is needed..
43590         //this.combo.onResize(w,h);
43591         
43592         if(typeof w != 'number'){
43593             // we do not handle it!?!?
43594             return;
43595         }
43596         var tw = this.combo.trigger.getWidth();
43597         tw += this.addicon ? this.addicon.getWidth() : 0;
43598         tw += this.editicon ? this.editicon.getWidth() : 0;
43599         var x = w - tw;
43600         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43601             
43602         this.combo.trigger.setStyle('left', '0px');
43603         
43604         if(this.list && this.listWidth === undefined){
43605             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43606             this.list.setWidth(lw);
43607             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43608         }
43609         
43610     
43611         
43612     },
43613     
43614     addItem: function(rec)
43615     {
43616         var valueField = this.combo.valueField;
43617         var displayField = this.combo.displayField;
43618         
43619         if (this.items.indexOfKey(rec[valueField]) > -1) {
43620             //console.log("GOT " + rec.data.id);
43621             return;
43622         }
43623         
43624         var x = new Roo.form.ComboBoxArray.Item({
43625             //id : rec[this.idField],
43626             data : rec,
43627             displayField : displayField ,
43628             tipField : displayField ,
43629             cb : this
43630         });
43631         // use the 
43632         this.items.add(rec[valueField],x);
43633         // add it before the element..
43634         this.updateHiddenEl();
43635         x.render(this.outerWrap, this.wrap.dom);
43636         // add the image handler..
43637     },
43638     
43639     updateHiddenEl : function()
43640     {
43641         this.validate();
43642         if (!this.hiddenEl) {
43643             return;
43644         }
43645         var ar = [];
43646         var idField = this.combo.valueField;
43647         
43648         this.items.each(function(f) {
43649             ar.push(f.data[idField]);
43650         });
43651         this.hiddenEl.dom.value = ar.join(this.seperator);
43652         this.validate();
43653     },
43654     
43655     reset : function()
43656     {
43657         this.items.clear();
43658         
43659         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43660            el.remove();
43661         });
43662         
43663         this.el.dom.value = '';
43664         if (this.hiddenEl) {
43665             this.hiddenEl.dom.value = '';
43666         }
43667         
43668     },
43669     getValue: function()
43670     {
43671         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43672     },
43673     setValue: function(v) // not a valid action - must use addItems..
43674     {
43675         
43676         this.reset();
43677          
43678         if (this.store.isLocal && (typeof(v) == 'string')) {
43679             // then we can use the store to find the values..
43680             // comma seperated at present.. this needs to allow JSON based encoding..
43681             this.hiddenEl.value  = v;
43682             var v_ar = [];
43683             Roo.each(v.split(this.seperator), function(k) {
43684                 Roo.log("CHECK " + this.valueField + ',' + k);
43685                 var li = this.store.query(this.valueField, k);
43686                 if (!li.length) {
43687                     return;
43688                 }
43689                 var add = {};
43690                 add[this.valueField] = k;
43691                 add[this.displayField] = li.item(0).data[this.displayField];
43692                 
43693                 this.addItem(add);
43694             }, this) 
43695              
43696         }
43697         if (typeof(v) == 'object' ) {
43698             // then let's assume it's an array of objects..
43699             Roo.each(v, function(l) {
43700                 var add = l;
43701                 if (typeof(l) == 'string') {
43702                     add = {};
43703                     add[this.valueField] = l;
43704                     add[this.displayField] = l
43705                 }
43706                 this.addItem(add);
43707             }, this);
43708              
43709         }
43710         
43711         
43712     },
43713     setFromData: function(v)
43714     {
43715         // this recieves an object, if setValues is called.
43716         this.reset();
43717         this.el.dom.value = v[this.displayField];
43718         this.hiddenEl.dom.value = v[this.valueField];
43719         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
43720             return;
43721         }
43722         var kv = v[this.valueField];
43723         var dv = v[this.displayField];
43724         kv = typeof(kv) != 'string' ? '' : kv;
43725         dv = typeof(dv) != 'string' ? '' : dv;
43726         
43727         
43728         var keys = kv.split(this.seperator);
43729         var display = dv.split(this.seperator);
43730         for (var i = 0 ; i < keys.length; i++) {
43731             add = {};
43732             add[this.valueField] = keys[i];
43733             add[this.displayField] = display[i];
43734             this.addItem(add);
43735         }
43736       
43737         
43738     },
43739     
43740     /**
43741      * Validates the combox array value
43742      * @return {Boolean} True if the value is valid, else false
43743      */
43744     validate : function(){
43745         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
43746             this.clearInvalid();
43747             return true;
43748         }
43749         return false;
43750     },
43751     
43752     validateValue : function(value){
43753         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
43754         
43755     },
43756     
43757     /*@
43758      * overide
43759      * 
43760      */
43761     isDirty : function() {
43762         if(this.disabled) {
43763             return false;
43764         }
43765         
43766         try {
43767             var d = Roo.decode(String(this.originalValue));
43768         } catch (e) {
43769             return String(this.getValue()) !== String(this.originalValue);
43770         }
43771         
43772         var originalValue = [];
43773         
43774         for (var i = 0; i < d.length; i++){
43775             originalValue.push(d[i][this.valueField]);
43776         }
43777         
43778         return String(this.getValue()) !== String(originalValue.join(this.seperator));
43779         
43780     }
43781     
43782 });
43783
43784
43785
43786 /**
43787  * @class Roo.form.ComboBoxArray.Item
43788  * @extends Roo.BoxComponent
43789  * A selected item in the list
43790  *  Fred [x]  Brian [x]  [Pick another |v]
43791  * 
43792  * @constructor
43793  * Create a new item.
43794  * @param {Object} config Configuration options
43795  */
43796  
43797 Roo.form.ComboBoxArray.Item = function(config) {
43798     config.id = Roo.id();
43799     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
43800 }
43801
43802 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
43803     data : {},
43804     cb: false,
43805     displayField : false,
43806     tipField : false,
43807     
43808     
43809     defaultAutoCreate : {
43810         tag: 'div',
43811         cls: 'x-cbarray-item',
43812         cn : [ 
43813             { tag: 'div' },
43814             {
43815                 tag: 'img',
43816                 width:16,
43817                 height : 16,
43818                 src : Roo.BLANK_IMAGE_URL ,
43819                 align: 'center'
43820             }
43821         ]
43822         
43823     },
43824     
43825  
43826     onRender : function(ct, position)
43827     {
43828         Roo.form.Field.superclass.onRender.call(this, ct, position);
43829         
43830         if(!this.el){
43831             var cfg = this.getAutoCreate();
43832             this.el = ct.createChild(cfg, position);
43833         }
43834         
43835         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
43836         
43837         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
43838             this.cb.renderer(this.data) :
43839             String.format('{0}',this.data[this.displayField]);
43840         
43841             
43842         this.el.child('div').dom.setAttribute('qtip',
43843                         String.format('{0}',this.data[this.tipField])
43844         );
43845         
43846         this.el.child('img').on('click', this.remove, this);
43847         
43848     },
43849    
43850     remove : function()
43851     {
43852         if(this.cb.disabled){
43853             return;
43854         }
43855         
43856         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
43857             this.cb.items.remove(this);
43858             this.el.child('img').un('click', this.remove, this);
43859             this.el.remove();
43860             this.cb.updateHiddenEl();
43861
43862             this.cb.fireEvent('remove', this.cb, this);
43863         }
43864         
43865     }
43866 });/*
43867  * RooJS Library 1.1.1
43868  * Copyright(c) 2008-2011  Alan Knowles
43869  *
43870  * License - LGPL
43871  */
43872  
43873
43874 /**
43875  * @class Roo.form.ComboNested
43876  * @extends Roo.form.ComboBox
43877  * A combobox for that allows selection of nested items in a list,
43878  * eg.
43879  *
43880  *  Book
43881  *    -> red
43882  *    -> green
43883  *  Table
43884  *    -> square
43885  *      ->red
43886  *      ->green
43887  *    -> rectangle
43888  *      ->green
43889  *      
43890  * 
43891  * @constructor
43892  * Create a new ComboNested
43893  * @param {Object} config Configuration options
43894  */
43895 Roo.form.ComboNested = function(config){
43896     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43897     // should verify some data...
43898     // like
43899     // hiddenName = required..
43900     // displayField = required
43901     // valudField == required
43902     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43903     var _t = this;
43904     Roo.each(req, function(e) {
43905         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43906             throw "Roo.form.ComboNested : missing value for: " + e;
43907         }
43908     });
43909      
43910     
43911 };
43912
43913 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
43914    
43915     /*
43916      * @config {Number} max Number of columns to show
43917      */
43918     
43919     maxColumns : 3,
43920    
43921     list : null, // the outermost div..
43922     innerLists : null, // the
43923     views : null,
43924     stores : null,
43925     // private
43926     loadingChildren : false,
43927     
43928     onRender : function(ct, position)
43929     {
43930         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
43931         
43932         if(this.hiddenName){
43933             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43934                     'before', true);
43935             this.hiddenField.value =
43936                 this.hiddenValue !== undefined ? this.hiddenValue :
43937                 this.value !== undefined ? this.value : '';
43938
43939             // prevent input submission
43940             this.el.dom.removeAttribute('name');
43941              
43942              
43943         }
43944         
43945         if(Roo.isGecko){
43946             this.el.dom.setAttribute('autocomplete', 'off');
43947         }
43948
43949         var cls = 'x-combo-list';
43950
43951         this.list = new Roo.Layer({
43952             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43953         });
43954
43955         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43956         this.list.setWidth(lw);
43957         this.list.swallowEvent('mousewheel');
43958         this.assetHeight = 0;
43959
43960         if(this.title){
43961             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43962             this.assetHeight += this.header.getHeight();
43963         }
43964         this.innerLists = [];
43965         this.views = [];
43966         this.stores = [];
43967         for (var i =0 ; i < this.maxColumns; i++) {
43968             this.onRenderList( cls, i);
43969         }
43970         
43971         // always needs footer, as we are going to have an 'OK' button.
43972         this.footer = this.list.createChild({cls:cls+'-ft'});
43973         this.pageTb = new Roo.Toolbar(this.footer);  
43974         var _this = this;
43975         this.pageTb.add(  {
43976             
43977             text: 'Done',
43978             handler: function()
43979             {
43980                 _this.collapse();
43981             }
43982         });
43983         
43984         if ( this.allowBlank && !this.disableClear) {
43985             
43986             this.pageTb.add(new Roo.Toolbar.Fill(), {
43987                 cls: 'x-btn-icon x-btn-clear',
43988                 text: '&#160;',
43989                 handler: function()
43990                 {
43991                     _this.collapse();
43992                     _this.clearValue();
43993                     _this.onSelect(false, -1);
43994                 }
43995             });
43996         }
43997         if (this.footer) {
43998             this.assetHeight += this.footer.getHeight();
43999         }
44000         
44001     },
44002     onRenderList : function (  cls, i)
44003     {
44004         
44005         var lw = Math.floor(
44006                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44007         );
44008         
44009         this.list.setWidth(lw); // default to '1'
44010
44011         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
44012         //il.on('mouseover', this.onViewOver, this, { list:  i });
44013         //il.on('mousemove', this.onViewMove, this, { list:  i });
44014         il.setWidth(lw);
44015         il.setStyle({ 'overflow-x' : 'hidden'});
44016
44017         if(!this.tpl){
44018             this.tpl = new Roo.Template({
44019                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44020                 isEmpty: function (value, allValues) {
44021                     //Roo.log(value);
44022                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44023                     return dl ? 'has-children' : 'no-children'
44024                 }
44025             });
44026         }
44027         
44028         var store  = this.store;
44029         if (i > 0) {
44030             store  = new Roo.data.SimpleStore({
44031                 //fields : this.store.reader.meta.fields,
44032                 reader : this.store.reader,
44033                 data : [ ]
44034             });
44035         }
44036         this.stores[i]  = store;
44037                   
44038         var view = this.views[i] = new Roo.View(
44039             il,
44040             this.tpl,
44041             {
44042                 singleSelect:true,
44043                 store: store,
44044                 selectedClass: this.selectedClass
44045             }
44046         );
44047         view.getEl().setWidth(lw);
44048         view.getEl().setStyle({
44049             position: i < 1 ? 'relative' : 'absolute',
44050             top: 0,
44051             left: (i * lw ) + 'px',
44052             display : i > 0 ? 'none' : 'block'
44053         });
44054         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44055         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44056         //view.on('click', this.onViewClick, this, { list : i });
44057
44058         store.on('beforeload', this.onBeforeLoad, this);
44059         store.on('load',  this.onLoad, this, { list  : i});
44060         store.on('loadexception', this.onLoadException, this);
44061
44062         // hide the other vies..
44063         
44064         
44065         
44066     },
44067       
44068     restrictHeight : function()
44069     {
44070         var mh = 0;
44071         Roo.each(this.innerLists, function(il,i) {
44072             var el = this.views[i].getEl();
44073             el.dom.style.height = '';
44074             var inner = el.dom;
44075             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44076             // only adjust heights on other ones..
44077             mh = Math.max(h, mh);
44078             if (i < 1) {
44079                 
44080                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44081                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44082                
44083             }
44084             
44085             
44086         }, this);
44087         
44088         this.list.beginUpdate();
44089         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44090         this.list.alignTo(this.el, this.listAlign);
44091         this.list.endUpdate();
44092         
44093     },
44094      
44095     
44096     // -- store handlers..
44097     // private
44098     onBeforeLoad : function()
44099     {
44100         if(!this.hasFocus){
44101             return;
44102         }
44103         this.innerLists[0].update(this.loadingText ?
44104                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44105         this.restrictHeight();
44106         this.selectedIndex = -1;
44107     },
44108     // private
44109     onLoad : function(a,b,c,d)
44110     {
44111         if (!this.loadingChildren) {
44112             // then we are loading the top level. - hide the children
44113             for (var i = 1;i < this.views.length; i++) {
44114                 this.views[i].getEl().setStyle({ display : 'none' });
44115             }
44116             var lw = Math.floor(
44117                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44118             );
44119         
44120              this.list.setWidth(lw); // default to '1'
44121
44122             
44123         }
44124         if(!this.hasFocus){
44125             return;
44126         }
44127         
44128         if(this.store.getCount() > 0) {
44129             this.expand();
44130             this.restrictHeight();   
44131         } else {
44132             this.onEmptyResults();
44133         }
44134         
44135         if (!this.loadingChildren) {
44136             this.selectActive();
44137         }
44138         /*
44139         this.stores[1].loadData([]);
44140         this.stores[2].loadData([]);
44141         this.views
44142         */    
44143     
44144         //this.el.focus();
44145     },
44146     
44147     
44148     // private
44149     onLoadException : function()
44150     {
44151         this.collapse();
44152         Roo.log(this.store.reader.jsonData);
44153         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44154             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44155         }
44156         
44157         
44158     },
44159     // no cleaning of leading spaces on blur here.
44160     cleanLeadingSpace : function(e) { },
44161     
44162
44163     onSelectChange : function (view, sels, opts )
44164     {
44165         var ix = view.getSelectedIndexes();
44166          
44167         if (opts.list > this.maxColumns - 2) {
44168             if (view.store.getCount()<  1) {
44169                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44170
44171             } else  {
44172                 if (ix.length) {
44173                     // used to clear ?? but if we are loading unselected 
44174                     this.setFromData(view.store.getAt(ix[0]).data);
44175                 }
44176                 
44177             }
44178             
44179             return;
44180         }
44181         
44182         if (!ix.length) {
44183             // this get's fired when trigger opens..
44184            // this.setFromData({});
44185             var str = this.stores[opts.list+1];
44186             str.data.clear(); // removeall wihtout the fire events..
44187             return;
44188         }
44189         
44190         var rec = view.store.getAt(ix[0]);
44191          
44192         this.setFromData(rec.data);
44193         this.fireEvent('select', this, rec, ix[0]);
44194         
44195         var lw = Math.floor(
44196              (
44197                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44198              ) / this.maxColumns
44199         );
44200         this.loadingChildren = true;
44201         this.stores[opts.list+1].loadDataFromChildren( rec );
44202         this.loadingChildren = false;
44203         var dl = this.stores[opts.list+1]. getTotalCount();
44204         
44205         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44206         
44207         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44208         for (var i = opts.list+2; i < this.views.length;i++) {
44209             this.views[i].getEl().setStyle({ display : 'none' });
44210         }
44211         
44212         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44213         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44214         
44215         if (this.isLoading) {
44216            // this.selectActive(opts.list);
44217         }
44218          
44219     },
44220     
44221     
44222     
44223     
44224     onDoubleClick : function()
44225     {
44226         this.collapse(); //??
44227     },
44228     
44229      
44230     
44231     
44232     
44233     // private
44234     recordToStack : function(store, prop, value, stack)
44235     {
44236         var cstore = new Roo.data.SimpleStore({
44237             //fields : this.store.reader.meta.fields, // we need array reader.. for
44238             reader : this.store.reader,
44239             data : [ ]
44240         });
44241         var _this = this;
44242         var record  = false;
44243         var srec = false;
44244         if(store.getCount() < 1){
44245             return false;
44246         }
44247         store.each(function(r){
44248             if(r.data[prop] == value){
44249                 record = r;
44250             srec = r;
44251                 return false;
44252             }
44253             if (r.data.cn && r.data.cn.length) {
44254                 cstore.loadDataFromChildren( r);
44255                 var cret = _this.recordToStack(cstore, prop, value, stack);
44256                 if (cret !== false) {
44257                     record = cret;
44258                     srec = r;
44259                     return false;
44260                 }
44261             }
44262              
44263             return true;
44264         });
44265         if (record == false) {
44266             return false
44267         }
44268         stack.unshift(srec);
44269         return record;
44270     },
44271     
44272     /*
44273      * find the stack of stores that match our value.
44274      *
44275      * 
44276      */
44277     
44278     selectActive : function ()
44279     {
44280         // if store is not loaded, then we will need to wait for that to happen first.
44281         var stack = [];
44282         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44283         for (var i = 0; i < stack.length; i++ ) {
44284             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44285         }
44286         
44287     }
44288         
44289          
44290     
44291     
44292     
44293     
44294 });/*
44295  * Based on:
44296  * Ext JS Library 1.1.1
44297  * Copyright(c) 2006-2007, Ext JS, LLC.
44298  *
44299  * Originally Released Under LGPL - original licence link has changed is not relivant.
44300  *
44301  * Fork - LGPL
44302  * <script type="text/javascript">
44303  */
44304 /**
44305  * @class Roo.form.Checkbox
44306  * @extends Roo.form.Field
44307  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44308  * @constructor
44309  * Creates a new Checkbox
44310  * @param {Object} config Configuration options
44311  */
44312 Roo.form.Checkbox = function(config){
44313     Roo.form.Checkbox.superclass.constructor.call(this, config);
44314     this.addEvents({
44315         /**
44316          * @event check
44317          * Fires when the checkbox is checked or unchecked.
44318              * @param {Roo.form.Checkbox} this This checkbox
44319              * @param {Boolean} checked The new checked value
44320              */
44321         check : true
44322     });
44323 };
44324
44325 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44326     /**
44327      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44328      */
44329     focusClass : undefined,
44330     /**
44331      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44332      */
44333     fieldClass: "x-form-field",
44334     /**
44335      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44336      */
44337     checked: false,
44338     /**
44339      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44340      * {tag: "input", type: "checkbox", autocomplete: "off"})
44341      */
44342     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44343     /**
44344      * @cfg {String} boxLabel The text that appears beside the checkbox
44345      */
44346     boxLabel : "",
44347     /**
44348      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44349      */  
44350     inputValue : '1',
44351     /**
44352      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44353      */
44354      valueOff: '0', // value when not checked..
44355
44356     actionMode : 'viewEl', 
44357     //
44358     // private
44359     itemCls : 'x-menu-check-item x-form-item',
44360     groupClass : 'x-menu-group-item',
44361     inputType : 'hidden',
44362     
44363     
44364     inSetChecked: false, // check that we are not calling self...
44365     
44366     inputElement: false, // real input element?
44367     basedOn: false, // ????
44368     
44369     isFormField: true, // not sure where this is needed!!!!
44370
44371     onResize : function(){
44372         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44373         if(!this.boxLabel){
44374             this.el.alignTo(this.wrap, 'c-c');
44375         }
44376     },
44377
44378     initEvents : function(){
44379         Roo.form.Checkbox.superclass.initEvents.call(this);
44380         this.el.on("click", this.onClick,  this);
44381         this.el.on("change", this.onClick,  this);
44382     },
44383
44384
44385     getResizeEl : function(){
44386         return this.wrap;
44387     },
44388
44389     getPositionEl : function(){
44390         return this.wrap;
44391     },
44392
44393     // private
44394     onRender : function(ct, position){
44395         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44396         /*
44397         if(this.inputValue !== undefined){
44398             this.el.dom.value = this.inputValue;
44399         }
44400         */
44401         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44402         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44403         var viewEl = this.wrap.createChild({ 
44404             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44405         this.viewEl = viewEl;   
44406         this.wrap.on('click', this.onClick,  this); 
44407         
44408         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44409         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44410         
44411         
44412         
44413         if(this.boxLabel){
44414             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44415         //    viewEl.on('click', this.onClick,  this); 
44416         }
44417         //if(this.checked){
44418             this.setChecked(this.checked);
44419         //}else{
44420             //this.checked = this.el.dom;
44421         //}
44422
44423     },
44424
44425     // private
44426     initValue : Roo.emptyFn,
44427
44428     /**
44429      * Returns the checked state of the checkbox.
44430      * @return {Boolean} True if checked, else false
44431      */
44432     getValue : function(){
44433         if(this.el){
44434             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44435         }
44436         return this.valueOff;
44437         
44438     },
44439
44440         // private
44441     onClick : function(){ 
44442         if (this.disabled) {
44443             return;
44444         }
44445         this.setChecked(!this.checked);
44446
44447         //if(this.el.dom.checked != this.checked){
44448         //    this.setValue(this.el.dom.checked);
44449        // }
44450     },
44451
44452     /**
44453      * Sets the checked state of the checkbox.
44454      * On is always based on a string comparison between inputValue and the param.
44455      * @param {Boolean/String} value - the value to set 
44456      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44457      */
44458     setValue : function(v,suppressEvent){
44459         
44460         
44461         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44462         //if(this.el && this.el.dom){
44463         //    this.el.dom.checked = this.checked;
44464         //    this.el.dom.defaultChecked = this.checked;
44465         //}
44466         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44467         //this.fireEvent("check", this, this.checked);
44468     },
44469     // private..
44470     setChecked : function(state,suppressEvent)
44471     {
44472         if (this.inSetChecked) {
44473             this.checked = state;
44474             return;
44475         }
44476         
44477     
44478         if(this.wrap){
44479             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44480         }
44481         this.checked = state;
44482         if(suppressEvent !== true){
44483             this.fireEvent('check', this, state);
44484         }
44485         this.inSetChecked = true;
44486         this.el.dom.value = state ? this.inputValue : this.valueOff;
44487         this.inSetChecked = false;
44488         
44489     },
44490     // handle setting of hidden value by some other method!!?!?
44491     setFromHidden: function()
44492     {
44493         if(!this.el){
44494             return;
44495         }
44496         //console.log("SET FROM HIDDEN");
44497         //alert('setFrom hidden');
44498         this.setValue(this.el.dom.value);
44499     },
44500     
44501     onDestroy : function()
44502     {
44503         if(this.viewEl){
44504             Roo.get(this.viewEl).remove();
44505         }
44506          
44507         Roo.form.Checkbox.superclass.onDestroy.call(this);
44508     },
44509     
44510     setBoxLabel : function(str)
44511     {
44512         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44513     }
44514
44515 });/*
44516  * Based on:
44517  * Ext JS Library 1.1.1
44518  * Copyright(c) 2006-2007, Ext JS, LLC.
44519  *
44520  * Originally Released Under LGPL - original licence link has changed is not relivant.
44521  *
44522  * Fork - LGPL
44523  * <script type="text/javascript">
44524  */
44525  
44526 /**
44527  * @class Roo.form.Radio
44528  * @extends Roo.form.Checkbox
44529  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44530  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44531  * @constructor
44532  * Creates a new Radio
44533  * @param {Object} config Configuration options
44534  */
44535 Roo.form.Radio = function(){
44536     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44537 };
44538 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44539     inputType: 'radio',
44540
44541     /**
44542      * If this radio is part of a group, it will return the selected value
44543      * @return {String}
44544      */
44545     getGroupValue : function(){
44546         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44547     },
44548     
44549     
44550     onRender : function(ct, position){
44551         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44552         
44553         if(this.inputValue !== undefined){
44554             this.el.dom.value = this.inputValue;
44555         }
44556          
44557         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44558         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44559         //var viewEl = this.wrap.createChild({ 
44560         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44561         //this.viewEl = viewEl;   
44562         //this.wrap.on('click', this.onClick,  this); 
44563         
44564         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44565         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44566         
44567         
44568         
44569         if(this.boxLabel){
44570             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44571         //    viewEl.on('click', this.onClick,  this); 
44572         }
44573          if(this.checked){
44574             this.el.dom.checked =   'checked' ;
44575         }
44576          
44577     } 
44578     
44579     
44580 });Roo.rtf = {}; // namespace
44581 Roo.rtf.Hex = function(hex)
44582 {
44583     this.hexstr = hex;
44584 };
44585 Roo.rtf.Paragraph = function(opts)
44586 {
44587     this.content = []; ///??? is that used?
44588 };Roo.rtf.Span = function(opts)
44589 {
44590     this.value = opts.value;
44591 };
44592
44593 Roo.rtf.Group = function(parent)
44594 {
44595     // we dont want to acutally store parent - it will make debug a nightmare..
44596     this.content = [];
44597     this.cn  = [];
44598      
44599        
44600     
44601 };
44602
44603 Roo.rtf.Group.prototype = {
44604     ignorable : false,
44605     content: false,
44606     cn: false,
44607     addContent : function(node) {
44608         // could set styles...
44609         this.content.push(node);
44610     },
44611     addChild : function(cn)
44612     {
44613         this.cn.push(cn);
44614     },
44615     // only for images really...
44616     toDataURL : function()
44617     {
44618         var mimetype = false;
44619         switch(true) {
44620             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
44621                 mimetype = "image/png";
44622                 break;
44623              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
44624                 mimetype = "image/jpeg";
44625                 break;
44626             default :
44627                 return 'about:blank'; // ?? error?
44628         }
44629         
44630         
44631         var hexstring = this.content[this.content.length-1].value;
44632         
44633         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
44634             return String.fromCharCode(parseInt(a, 16));
44635         }).join(""));
44636     }
44637     
44638 };
44639 // this looks like it's normally the {rtf{ .... }}
44640 Roo.rtf.Document = function()
44641 {
44642     // we dont want to acutally store parent - it will make debug a nightmare..
44643     this.rtlch  = [];
44644     this.content = [];
44645     this.cn = [];
44646     
44647 };
44648 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
44649     addChild : function(cn)
44650     {
44651         this.cn.push(cn);
44652         switch(cn.type) {
44653             case 'rtlch': // most content seems to be inside this??
44654             case 'listtext':
44655             case 'shpinst':
44656                 this.rtlch.push(cn);
44657                 return;
44658             default:
44659                 this[cn.type] = cn;
44660         }
44661         
44662     },
44663     
44664     getElementsByType : function(type)
44665     {
44666         var ret =  [];
44667         this._getElementsByType(type, ret, this.cn, 'rtf');
44668         return ret;
44669     },
44670     _getElementsByType : function (type, ret, search_array, path)
44671     {
44672         search_array.forEach(function(n,i) {
44673             if (n.type == type) {
44674                 n.path = path + '/' + n.type + ':' + i;
44675                 ret.push(n);
44676             }
44677             if (n.cn.length > 0) {
44678                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
44679             }
44680         },this);
44681     }
44682     
44683 });
44684  
44685 Roo.rtf.Ctrl = function(opts)
44686 {
44687     this.value = opts.value;
44688     this.param = opts.param;
44689 };
44690 /**
44691  *
44692  *
44693  * based on this https://github.com/iarna/rtf-parser
44694  * it's really only designed to extract pict from pasted RTF 
44695  *
44696  * usage:
44697  *
44698  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
44699  *  
44700  *
44701  */
44702
44703  
44704
44705
44706
44707 Roo.rtf.Parser = function(text) {
44708     //super({objectMode: true})
44709     this.text = '';
44710     this.parserState = this.parseText;
44711     
44712     // these are for interpeter...
44713     this.doc = {};
44714     ///this.parserState = this.parseTop
44715     this.groupStack = [];
44716     this.hexStore = [];
44717     this.doc = false;
44718     
44719     this.groups = []; // where we put the return.
44720     
44721     for (var ii = 0; ii < text.length; ++ii) {
44722         ++this.cpos;
44723         
44724         if (text[ii] === '\n') {
44725             ++this.row;
44726             this.col = 1;
44727         } else {
44728             ++this.col;
44729         }
44730         this.parserState(text[ii]);
44731     }
44732     
44733     
44734     
44735 };
44736 Roo.rtf.Parser.prototype = {
44737     text : '', // string being parsed..
44738     controlWord : '',
44739     controlWordParam :  '',
44740     hexChar : '',
44741     doc : false,
44742     group: false,
44743     groupStack : false,
44744     hexStore : false,
44745     
44746     
44747     cpos : 0, 
44748     row : 1, // reportin?
44749     col : 1, //
44750
44751      
44752     push : function (el)
44753     {
44754         var m = 'cmd'+ el.type;
44755         if (typeof(this[m]) == 'undefined') {
44756             Roo.log('invalid cmd:' + el.type);
44757             return;
44758         }
44759         this[m](el);
44760         //Roo.log(el);
44761     },
44762     flushHexStore : function()
44763     {
44764         if (this.hexStore.length < 1) {
44765             return;
44766         }
44767         var hexstr = this.hexStore.map(
44768             function(cmd) {
44769                 return cmd.value;
44770         }).join('');
44771         
44772         this.group.addContent( new Roo.rtf.Hex( hexstr ));
44773               
44774             
44775         this.hexStore.splice(0)
44776         
44777     },
44778     
44779     cmdgroupstart : function()
44780     {
44781         this.flushHexStore();
44782         if (this.group) {
44783             this.groupStack.push(this.group);
44784         }
44785          // parent..
44786         if (this.doc === false) {
44787             this.group = this.doc = new Roo.rtf.Document();
44788             return;
44789             
44790         }
44791         this.group = new Roo.rtf.Group(this.group);
44792     },
44793     cmdignorable : function()
44794     {
44795         this.flushHexStore();
44796         this.group.ignorable = true;
44797     },
44798     cmdendparagraph : function()
44799     {
44800         this.flushHexStore();
44801         this.group.addContent(new Roo.rtf.Paragraph());
44802     },
44803     cmdgroupend : function ()
44804     {
44805         this.flushHexStore();
44806         var endingGroup = this.group;
44807         
44808         
44809         this.group = this.groupStack.pop();
44810         if (this.group) {
44811             this.group.addChild(endingGroup);
44812         }
44813         
44814         
44815         
44816         var doc = this.group || this.doc;
44817         //if (endingGroup instanceof FontTable) {
44818         //  doc.fonts = endingGroup.table
44819         //} else if (endingGroup instanceof ColorTable) {
44820         //  doc.colors = endingGroup.table
44821         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
44822         if (endingGroup.ignorable === false) {
44823             //code
44824             this.groups.push(endingGroup);
44825            // Roo.log( endingGroup );
44826         }
44827             //Roo.each(endingGroup.content, function(item)) {
44828             //    doc.addContent(item);
44829             //}
44830             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
44831         //}
44832     },
44833     cmdtext : function (cmd)
44834     {
44835         this.flushHexStore();
44836         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
44837             //this.group = this.doc
44838         }
44839         this.group.addContent(new Roo.rtf.Span(cmd));
44840     },
44841     cmdcontrolword : function (cmd)
44842     {
44843         this.flushHexStore();
44844         if (!this.group.type) {
44845             this.group.type = cmd.value;
44846             return;
44847         }
44848         this.group.addContent(new Roo.rtf.Ctrl(cmd));
44849         // we actually don't care about ctrl words...
44850         return ;
44851         /*
44852         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
44853         if (this[method]) {
44854             this[method](cmd.param)
44855         } else {
44856             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
44857         }
44858         */
44859     },
44860     cmdhexchar : function(cmd) {
44861         this.hexStore.push(cmd);
44862     },
44863     cmderror : function(cmd) {
44864         throw new Exception (cmd.value);
44865     },
44866     
44867     /*
44868       _flush (done) {
44869         if (this.text !== '\u0000') this.emitText()
44870         done()
44871       }
44872       */
44873       
44874       
44875     parseText : function(c)
44876     {
44877         if (c === '\\') {
44878             this.parserState = this.parseEscapes;
44879         } else if (c === '{') {
44880             this.emitStartGroup();
44881         } else if (c === '}') {
44882             this.emitEndGroup();
44883         } else if (c === '\x0A' || c === '\x0D') {
44884             // cr/lf are noise chars
44885         } else {
44886             this.text += c;
44887         }
44888     },
44889     
44890     parseEscapes: function (c)
44891     {
44892         if (c === '\\' || c === '{' || c === '}') {
44893             this.text += c;
44894             this.parserState = this.parseText;
44895         } else {
44896             this.parserState = this.parseControlSymbol;
44897             this.parseControlSymbol(c);
44898         }
44899     },
44900     parseControlSymbol: function(c)
44901     {
44902         if (c === '~') {
44903             this.text += '\u00a0'; // nbsp
44904             this.parserState = this.parseText
44905         } else if (c === '-') {
44906              this.text += '\u00ad'; // soft hyphen
44907         } else if (c === '_') {
44908             this.text += '\u2011'; // non-breaking hyphen
44909         } else if (c === '*') {
44910             this.emitIgnorable();
44911             this.parserState = this.parseText;
44912         } else if (c === "'") {
44913             this.parserState = this.parseHexChar;
44914         } else if (c === '|') { // formula cacter
44915             this.emitFormula();
44916             this.parserState = this.parseText;
44917         } else if (c === ':') { // subentry in an index entry
44918             this.emitIndexSubEntry();
44919             this.parserState = this.parseText;
44920         } else if (c === '\x0a') {
44921             this.emitEndParagraph();
44922             this.parserState = this.parseText;
44923         } else if (c === '\x0d') {
44924             this.emitEndParagraph();
44925             this.parserState = this.parseText;
44926         } else {
44927             this.parserState = this.parseControlWord;
44928             this.parseControlWord(c);
44929         }
44930     },
44931     parseHexChar: function (c)
44932     {
44933         if (/^[A-Fa-f0-9]$/.test(c)) {
44934             this.hexChar += c;
44935             if (this.hexChar.length >= 2) {
44936               this.emitHexChar();
44937               this.parserState = this.parseText;
44938             }
44939             return;
44940         }
44941         this.emitError("Invalid character \"" + c + "\" in hex literal.");
44942         this.parserState = this.parseText;
44943         
44944     },
44945     parseControlWord : function(c)
44946     {
44947         if (c === ' ') {
44948             this.emitControlWord();
44949             this.parserState = this.parseText;
44950         } else if (/^[-\d]$/.test(c)) {
44951             this.parserState = this.parseControlWordParam;
44952             this.controlWordParam += c;
44953         } else if (/^[A-Za-z]$/.test(c)) {
44954           this.controlWord += c;
44955         } else {
44956           this.emitControlWord();
44957           this.parserState = this.parseText;
44958           this.parseText(c);
44959         }
44960     },
44961     parseControlWordParam : function (c) {
44962         if (/^\d$/.test(c)) {
44963           this.controlWordParam += c;
44964         } else if (c === ' ') {
44965           this.emitControlWord();
44966           this.parserState = this.parseText;
44967         } else {
44968           this.emitControlWord();
44969           this.parserState = this.parseText;
44970           this.parseText(c);
44971         }
44972     },
44973     
44974     
44975     
44976     
44977     emitText : function () {
44978         if (this.text === '') {
44979             return;
44980         }
44981         this.push({
44982             type: 'text',
44983             value: this.text,
44984             pos: this.cpos,
44985             row: this.row,
44986             col: this.col
44987         });
44988         this.text = ''
44989     },
44990     emitControlWord : function ()
44991     {
44992         this.emitText();
44993         if (this.controlWord === '') {
44994             this.emitError('empty control word');
44995         } else {
44996             this.push({
44997                   type: 'controlword',
44998                   value: this.controlWord,
44999                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
45000                   pos: this.cpos,
45001                   row: this.row,
45002                   col: this.col
45003             });
45004         }
45005         this.controlWord = '';
45006         this.controlWordParam = '';
45007     },
45008     emitStartGroup : function ()
45009     {
45010         this.emitText();
45011         this.push({
45012             type: 'groupstart',
45013             pos: this.cpos,
45014             row: this.row,
45015             col: this.col
45016         });
45017     },
45018     emitEndGroup : function ()
45019     {
45020         this.emitText();
45021         this.push({
45022             type: 'groupend',
45023             pos: this.cpos,
45024             row: this.row,
45025             col: this.col
45026         });
45027     },
45028     emitIgnorable : function ()
45029     {
45030         this.emitText();
45031         this.push({
45032             type: 'ignorable',
45033             pos: this.cpos,
45034             row: this.row,
45035             col: this.col
45036         });
45037     },
45038     emitHexChar : function ()
45039     {
45040         this.emitText();
45041         this.push({
45042             type: 'hexchar',
45043             value: this.hexChar,
45044             pos: this.cpos,
45045             row: this.row,
45046             col: this.col
45047         });
45048         this.hexChar = ''
45049     },
45050     emitError : function (message)
45051     {
45052       this.emitText();
45053       this.push({
45054             type: 'error',
45055             value: message,
45056             row: this.row,
45057             col: this.col,
45058             char: this.cpos //,
45059             //stack: new Error().stack
45060         });
45061     },
45062     emitEndParagraph : function () {
45063         this.emitText();
45064         this.push({
45065             type: 'endparagraph',
45066             pos: this.cpos,
45067             row: this.row,
45068             col: this.col
45069         });
45070     }
45071      
45072 } ;
45073 Roo.htmleditor = {};
45074  
45075 /**
45076  * @class Roo.htmleditor.Filter
45077  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45078  * @cfg {DomElement} node The node to iterate and filter
45079  * @cfg {boolean|String|Array} tag Tags to replace 
45080  * @constructor
45081  * Create a new Filter.
45082  * @param {Object} config Configuration options
45083  */
45084
45085
45086
45087 Roo.htmleditor.Filter = function(cfg) {
45088     Roo.apply(this.cfg);
45089     // this does not actually call walk as it's really just a abstract class
45090 }
45091
45092
45093 Roo.htmleditor.Filter.prototype = {
45094     
45095     node: false,
45096     
45097     tag: false,
45098
45099     // overrride to do replace comments.
45100     replaceComment : false,
45101     
45102     // overrride to do replace or do stuff with tags..
45103     replaceTag : false,
45104     
45105     walk : function(dom)
45106     {
45107         Roo.each( Array.from(dom.childNodes), function( e ) {
45108             switch(true) {
45109                 
45110                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
45111                     this.replaceComment(e);
45112                     return;
45113                 
45114                 case e.nodeType != 1: //not a node.
45115                     return;
45116                 
45117                 case this.tag === true: // everything
45118                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45119                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45120                     if (this.replaceTag && false === this.replaceTag(e)) {
45121                         return;
45122                     }
45123                     if (e.hasChildNodes()) {
45124                         this.walk(e);
45125                     }
45126                     return;
45127                 
45128                 default:    // tags .. that do not match.
45129                     if (e.hasChildNodes()) {
45130                         this.walk(e);
45131                     }
45132             }
45133             
45134         }, this);
45135         
45136     }
45137 }; 
45138
45139 /**
45140  * @class Roo.htmleditor.FilterAttributes
45141  * clean attributes and  styles including http:// etc.. in attribute
45142  * @constructor
45143 * Run a new Attribute Filter
45144 * @param {Object} config Configuration options
45145  */
45146 Roo.htmleditor.FilterAttributes = function(cfg)
45147 {
45148     Roo.apply(this, cfg);
45149     this.attrib_black = this.attrib_black || [];
45150     this.attrib_white = this.attrib_white || [];
45151
45152     this.attrib_clean = this.attrib_clean || [];
45153     this.style_white = this.style_white || [];
45154     this.style_black = this.style_black || [];
45155     this.walk(cfg.node);
45156 }
45157
45158 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45159 {
45160     tag: true, // all tags
45161     
45162     attrib_black : false, // array
45163     attrib_clean : false,
45164     attrib_white : false,
45165
45166     style_white : false,
45167     style_black : false,
45168      
45169      
45170     replaceTag : function(node)
45171     {
45172         if (!node.attributes || !node.attributes.length) {
45173             return true;
45174         }
45175         
45176         for (var i = node.attributes.length-1; i > -1 ; i--) {
45177             var a = node.attributes[i];
45178             //console.log(a);
45179             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45180                 node.removeAttribute(a.name);
45181                 continue;
45182             }
45183             
45184             
45185             
45186             if (a.name.toLowerCase().substr(0,2)=='on')  {
45187                 node.removeAttribute(a.name);
45188                 continue;
45189             }
45190             
45191             
45192             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45193                 node.removeAttribute(a.name);
45194                 continue;
45195             }
45196             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45197                 this.cleanAttr(node,a.name,a.value); // fixme..
45198                 continue;
45199             }
45200             if (a.name == 'style') {
45201                 this.cleanStyle(node,a.name,a.value);
45202                 continue;
45203             }
45204             /// clean up MS crap..
45205             // tecnically this should be a list of valid class'es..
45206             
45207             
45208             if (a.name == 'class') {
45209                 if (a.value.match(/^Mso/)) {
45210                     node.removeAttribute('class');
45211                 }
45212                 
45213                 if (a.value.match(/^body$/)) {
45214                     node.removeAttribute('class');
45215                 }
45216                 continue;
45217             }
45218             
45219             
45220             // style cleanup!?
45221             // class cleanup?
45222             
45223         }
45224         return true; // clean children
45225     },
45226         
45227     cleanAttr: function(node, n,v)
45228     {
45229         
45230         if (v.match(/^\./) || v.match(/^\//)) {
45231             return;
45232         }
45233         if (v.match(/^(http|https):\/\//)
45234             || v.match(/^mailto:/) 
45235             || v.match(/^ftp:/)
45236             || v.match(/^data:/)
45237             ) {
45238             return;
45239         }
45240         if (v.match(/^#/)) {
45241             return;
45242         }
45243         if (v.match(/^\{/)) { // allow template editing.
45244             return;
45245         }
45246 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45247         node.removeAttribute(n);
45248         
45249     },
45250     cleanStyle : function(node,  n,v)
45251     {
45252         if (v.match(/expression/)) { //XSS?? should we even bother..
45253             node.removeAttribute(n);
45254             return;
45255         }
45256         
45257         var parts = v.split(/;/);
45258         var clean = [];
45259         
45260         Roo.each(parts, function(p) {
45261             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45262             if (!p.length) {
45263                 return true;
45264             }
45265             var l = p.split(':').shift().replace(/\s+/g,'');
45266             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45267             
45268             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45269                 return true;
45270             }
45271             //Roo.log()
45272             // only allow 'c whitelisted system attributes'
45273             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45274                 return true;
45275             }
45276             
45277             
45278             clean.push(p);
45279             return true;
45280         },this);
45281         if (clean.length) { 
45282             node.setAttribute(n, clean.join(';'));
45283         } else {
45284             node.removeAttribute(n);
45285         }
45286         
45287     }
45288         
45289         
45290         
45291     
45292 });/**
45293  * @class Roo.htmleditor.FilterBlack
45294  * remove blacklisted elements.
45295  * @constructor
45296  * Run a new Blacklisted Filter
45297  * @param {Object} config Configuration options
45298  */
45299
45300 Roo.htmleditor.FilterBlack = function(cfg)
45301 {
45302     Roo.apply(this, cfg);
45303     this.walk(cfg.node);
45304 }
45305
45306 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45307 {
45308     tag : true, // all elements.
45309    
45310     replace : function(n)
45311     {
45312         n.parentNode.removeChild(n);
45313     }
45314 });
45315 /**
45316  * @class Roo.htmleditor.FilterComment
45317  * remove comments.
45318  * @constructor
45319 * Run a new Comments Filter
45320 * @param {Object} config Configuration options
45321  */
45322 Roo.htmleditor.FilterComment = function(cfg)
45323 {
45324     this.walk(cfg.node);
45325 }
45326
45327 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45328 {
45329   
45330     replaceComment : function(n)
45331     {
45332         n.parentNode.removeChild(n);
45333     }
45334 });/**
45335  * @class Roo.htmleditor.FilterKeepChildren
45336  * remove tags but keep children
45337  * @constructor
45338  * Run a new Keep Children Filter
45339  * @param {Object} config Configuration options
45340  */
45341
45342 Roo.htmleditor.FilterKeepChildren = function(cfg)
45343 {
45344     Roo.apply(this, cfg);
45345     if (this.tag === false) {
45346         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45347     }
45348     this.walk(cfg.node);
45349 }
45350
45351 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45352 {
45353     
45354   
45355     replaceTag : function(node)
45356     {
45357         // walk children...
45358         //Roo.log(node);
45359         var ar = Array.from(node.childNodes);
45360         //remove first..
45361         for (var i = 0; i < ar.length; i++) {
45362             if (ar[i].nodeType == 1) {
45363                 if (
45364                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45365                     || // array and it matches
45366                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45367                 ) {
45368                     this.replaceTag(ar[i]); // child is blacklisted as well...
45369                     continue;
45370                 }
45371             }
45372         }  
45373         ar = Array.from(node.childNodes);
45374         for (var i = 0; i < ar.length; i++) {
45375          
45376             node.removeChild(ar[i]);
45377             // what if we need to walk these???
45378             node.parentNode.insertBefore(ar[i], node);
45379             if (this.tag !== false) {
45380                 this.walk(ar[i]);
45381                 
45382             }
45383         }
45384         node.parentNode.removeChild(node);
45385         return false; // don't walk children
45386         
45387         
45388     }
45389 });/**
45390  * @class Roo.htmleditor.FilterParagraph
45391  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45392  * like on 'push' to remove the <p> tags and replace them with line breaks.
45393  * @constructor
45394  * Run a new Paragraph Filter
45395  * @param {Object} config Configuration options
45396  */
45397
45398 Roo.htmleditor.FilterParagraph = function(cfg)
45399 {
45400     // no need to apply config.
45401     this.walk(cfg.node);
45402 }
45403
45404 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45405 {
45406     
45407      
45408     tag : 'P',
45409     
45410      
45411     replaceTag : function(node)
45412     {
45413         
45414         if (node.childNodes.length == 1 &&
45415             node.childNodes[0].nodeType == 3 &&
45416             node.childNodes[0].textContent.trim().length < 1
45417             ) {
45418             // remove and replace with '<BR>';
45419             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45420             return false; // no need to walk..
45421         }
45422         var ar = Array.from(node.childNodes);
45423         for (var i = 0; i < ar.length; i++) {
45424             node.removeChild(ar[i]);
45425             // what if we need to walk these???
45426             node.parentNode.insertBefore(ar[i], node);
45427         }
45428         // now what about this?
45429         // <p> &nbsp; </p>
45430         
45431         // double BR.
45432         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45433         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45434         node.parentNode.removeChild(node);
45435         
45436         return false;
45437
45438     }
45439     
45440 });/**
45441  * @class Roo.htmleditor.FilterSpan
45442  * filter span's with no attributes out..
45443  * @constructor
45444  * Run a new Span Filter
45445  * @param {Object} config Configuration options
45446  */
45447
45448 Roo.htmleditor.FilterSpan = function(cfg)
45449 {
45450     // no need to apply config.
45451     this.walk(cfg.node);
45452 }
45453
45454 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45455 {
45456      
45457     tag : 'SPAN',
45458      
45459  
45460     replaceTag : function(node)
45461     {
45462         if (node.attributes && node.attributes.length > 0) {
45463             return true; // walk if there are any.
45464         }
45465         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
45466         return false;
45467      
45468     }
45469     
45470 });/**
45471  * @class Roo.htmleditor.FilterTableWidth
45472   try and remove table width data - as that frequently messes up other stuff.
45473  * 
45474  *      was cleanTableWidths.
45475  *
45476  * Quite often pasting from word etc.. results in tables with column and widths.
45477  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45478  *
45479  * @constructor
45480  * Run a new Table Filter
45481  * @param {Object} config Configuration options
45482  */
45483
45484 Roo.htmleditor.FilterTableWidth = function(cfg)
45485 {
45486     // no need to apply config.
45487     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
45488     this.walk(cfg.node);
45489 }
45490
45491 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
45492 {
45493      
45494      
45495     
45496     replaceTag: function(node) {
45497         
45498         
45499       
45500         if (node.hasAttribute('width')) {
45501             node.removeAttribute('width');
45502         }
45503         
45504          
45505         if (node.hasAttribute("style")) {
45506             // pretty basic...
45507             
45508             var styles = node.getAttribute("style").split(";");
45509             var nstyle = [];
45510             Roo.each(styles, function(s) {
45511                 if (!s.match(/:/)) {
45512                     return;
45513                 }
45514                 var kv = s.split(":");
45515                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45516                     return;
45517                 }
45518                 // what ever is left... we allow.
45519                 nstyle.push(s);
45520             });
45521             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45522             if (!nstyle.length) {
45523                 node.removeAttribute('style');
45524             }
45525         }
45526         
45527         return true; // continue doing children..
45528     }
45529 });/**
45530  * @class Roo.htmleditor.FilterWord
45531  * try and clean up all the mess that Word generates.
45532  * 
45533  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
45534  
45535  * @constructor
45536  * Run a new Span Filter
45537  * @param {Object} config Configuration options
45538  */
45539
45540 Roo.htmleditor.FilterWord = function(cfg)
45541 {
45542     // no need to apply config.
45543     this.walk(cfg.node);
45544 }
45545
45546 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
45547 {
45548     tag: true,
45549      
45550     
45551     /**
45552      * Clean up MS wordisms...
45553      */
45554     replaceTag : function(node)
45555     {
45556          
45557         // no idea what this does - span with text, replaceds with just text.
45558         if(
45559                 node.nodeName == 'SPAN' &&
45560                 !node.hasAttributes() &&
45561                 node.childNodes.length == 1 &&
45562                 node.firstChild.nodeName == "#text"  
45563         ) {
45564             var textNode = node.firstChild;
45565             node.removeChild(textNode);
45566             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45567                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
45568             }
45569             node.parentNode.insertBefore(textNode, node);
45570             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45571                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
45572             }
45573             
45574             node.parentNode.removeChild(node);
45575             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
45576         }
45577         
45578    
45579         
45580         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
45581             node.parentNode.removeChild(node);
45582             return false; // dont do chidlren
45583         }
45584         //Roo.log(node.tagName);
45585         // remove - but keep children..
45586         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
45587             //Roo.log('-- removed');
45588             while (node.childNodes.length) {
45589                 var cn = node.childNodes[0];
45590                 node.removeChild(cn);
45591                 node.parentNode.insertBefore(cn, node);
45592                 // move node to parent - and clean it..
45593                 this.replaceTag(cn);
45594             }
45595             node.parentNode.removeChild(node);
45596             /// no need to iterate chidlren = it's got none..
45597             //this.iterateChildren(node, this.cleanWord);
45598             return false; // no need to iterate children.
45599         }
45600         // clean styles
45601         if (node.className.length) {
45602             
45603             var cn = node.className.split(/\W+/);
45604             var cna = [];
45605             Roo.each(cn, function(cls) {
45606                 if (cls.match(/Mso[a-zA-Z]+/)) {
45607                     return;
45608                 }
45609                 cna.push(cls);
45610             });
45611             node.className = cna.length ? cna.join(' ') : '';
45612             if (!cna.length) {
45613                 node.removeAttribute("class");
45614             }
45615         }
45616         
45617         if (node.hasAttribute("lang")) {
45618             node.removeAttribute("lang");
45619         }
45620         
45621         if (node.hasAttribute("style")) {
45622             
45623             var styles = node.getAttribute("style").split(";");
45624             var nstyle = [];
45625             Roo.each(styles, function(s) {
45626                 if (!s.match(/:/)) {
45627                     return;
45628                 }
45629                 var kv = s.split(":");
45630                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45631                     return;
45632                 }
45633                 // what ever is left... we allow.
45634                 nstyle.push(s);
45635             });
45636             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45637             if (!nstyle.length) {
45638                 node.removeAttribute('style');
45639             }
45640         }
45641         return true; // do children
45642         
45643         
45644         
45645     }
45646 });
45647 /**
45648  * @class Roo.htmleditor.FilterStyleToTag
45649  * part of the word stuff... - certain 'styles' should be converted to tags.
45650  * eg.
45651  *   font-weight: bold -> bold
45652  *   ?? super / subscrit etc..
45653  * 
45654  * @constructor
45655 * Run a new style to tag filter.
45656 * @param {Object} config Configuration options
45657  */
45658 Roo.htmleditor.FilterStyleToTag = function(cfg)
45659 {
45660     
45661     this.tags = {
45662         B  : [ 'fontWeight' , 'bold'],
45663         I :  [ 'fontStyle' , 'italic'],
45664         //pre :  [ 'font-style' , 'italic'],
45665         // h1.. h6 ?? font-size?
45666         SUP : [ 'verticalAlign' , 'super' ],
45667         SUB : [ 'verticalAlign' , 'sub' ]
45668         
45669         
45670     };
45671     
45672     Roo.apply(this, cfg);
45673      
45674     
45675     this.walk(cfg.node);
45676     
45677     
45678     
45679 }
45680
45681
45682 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
45683 {
45684     tag: true, // all tags
45685     
45686     tags : false,
45687     
45688     
45689     replaceTag : function(node)
45690     {
45691         
45692         
45693         if (node.getAttribute("style") === null) {
45694             return true;
45695         }
45696         var inject = [];
45697         for (var k in this.tags) {
45698             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
45699                 inject.push(k);
45700                 node.style.removeProperty(this.tags[k][0]);
45701             }
45702         }
45703         if (!inject.length) {
45704             return true; 
45705         }
45706         var cn = Array.from(node.childNodes);
45707         var nn = node;
45708         Roo.each(inject, function(t) {
45709             var nc = node.ownerDocument.createelement(t);
45710             nn.appendChild(nc);
45711             nn = nc;
45712         });
45713         for(var i = 0;i < cn.length;cn++) {
45714             node.removeChild(cn[i]);
45715             nn.appendChild(cn[i]);
45716         }
45717         return true /// iterate thru
45718     }
45719     
45720 })/**
45721  * @class Roo.htmleditor.FilterLongBr
45722  * BR/BR/BR - keep a maximum of 2...
45723  * @constructor
45724  * Run a new Long BR Filter
45725  * @param {Object} config Configuration options
45726  */
45727
45728 Roo.htmleditor.FilterLongBr = function(cfg)
45729 {
45730     // no need to apply config.
45731     this.walk(cfg.node);
45732 }
45733
45734 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
45735 {
45736     
45737      
45738     tag : 'BR',
45739     
45740      
45741     replaceTag : function(node)
45742     {
45743         
45744         var ps = node.nextSibling;
45745         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
45746             ps = ps.nextSibling;
45747         }
45748         
45749         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
45750             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
45751             return false;
45752         }
45753         
45754         if (!ps || ps.nodeType != 1) {
45755             return false;
45756         }
45757         
45758         if (!ps || ps.tagName != 'BR') {
45759            
45760             return false;
45761         }
45762         
45763         
45764         
45765         
45766         
45767         if (!node.previousSibling) {
45768             return false;
45769         }
45770         var ps = node.previousSibling;
45771         
45772         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
45773             ps = ps.previousSibling;
45774         }
45775         if (!ps || ps.nodeType != 1) {
45776             return false;
45777         }
45778         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
45779         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
45780             return false;
45781         }
45782         
45783         node.parentNode.removeChild(node); // remove me...
45784         
45785         return false; // no need to do children
45786
45787     }
45788     
45789 });
45790 /**
45791  * @class Roo.htmleditor.Tidy
45792  * Tidy HTML 
45793  * @cfg {Roo.HtmlEditorCore} core the editor.
45794  * @constructor
45795  * Create a new Filter.
45796  * @param {Object} config Configuration options
45797  */
45798
45799
45800 Roo.htmleditor.Tidy = function(cfg) {
45801     Roo.apply(this, cfg);
45802     
45803     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
45804      
45805 }
45806
45807 Roo.htmleditor.Tidy.toString = function(node)
45808 {
45809     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
45810 }
45811
45812 Roo.htmleditor.Tidy.prototype = {
45813     
45814     
45815     wrap : function(s) {
45816         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
45817     },
45818
45819     
45820     tidy : function(node, indent) {
45821      
45822         if  (node.nodeType == 3) {
45823             // text.
45824             
45825             
45826             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
45827                 
45828             
45829         }
45830         
45831         if  (node.nodeType != 1) {
45832             return '';
45833         }
45834         
45835         
45836         
45837         if (node.tagName == 'BODY') {
45838             
45839             return this.cn(node, '');
45840         }
45841              
45842              // Prints the node tagName, such as <A>, <IMG>, etc
45843         var ret = "<" + node.tagName +  this.attr(node) ;
45844         
45845         // elements with no children..
45846         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
45847                 return ret + '/>';
45848         }
45849         ret += '>';
45850         
45851         
45852         var cindent = indent === false ? '' : (indent + '  ');
45853         // tags where we will not pad the children.. (inline text tags etc..)
45854         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
45855             cindent = false;
45856             
45857             
45858         }
45859         
45860         var cn = this.cn(node, cindent );
45861         
45862         return ret + cn  + '</' + node.tagName + '>';
45863         
45864     },
45865     cn: function(node, indent)
45866     {
45867         var ret = [];
45868         
45869         var ar = Array.from(node.childNodes);
45870         for (var i = 0 ; i < ar.length ; i++) {
45871             
45872             
45873             
45874             if (indent !== false   // indent==false preservies everything
45875                 && i > 0
45876                 && ar[i].nodeType == 3 
45877                 && ar[i].nodeValue.length > 0
45878                 && ar[i].nodeValue.match(/^\s+/)
45879             ) {
45880                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
45881                     ret.pop(); // remove line break from last?
45882                 }
45883                 
45884                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
45885             }
45886             if (indent !== false
45887                 && ar[i].nodeType == 1 // element - and indent is not set... 
45888             ) {
45889                 ret.push("\n" + indent); 
45890             }
45891             
45892             ret.push(this.tidy(ar[i], indent));
45893             // text + trailing indent 
45894             if (indent !== false
45895                 && ar[i].nodeType == 3
45896                 && ar[i].nodeValue.length > 0
45897                 && ar[i].nodeValue.match(/\s+$/)
45898             ){
45899                 ret.push("\n" + indent); 
45900             }
45901             
45902             
45903             
45904             
45905         }
45906         // what if all text?
45907         
45908         
45909         return ret.join('');
45910     },
45911     
45912          
45913         
45914     attr : function(node)
45915     {
45916         var attr = [];
45917         for(i = 0; i < node.attributes.length;i++) {
45918             
45919             // skip empty values?
45920             if (!node.attributes.item(i).value.length) {
45921                 continue;
45922             }
45923             attr.push(  node.attributes.item(i).name + '="' +
45924                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
45925             );
45926         }
45927         return attr.length ? (' ' + attr.join(' ') ) : '';
45928         
45929     }
45930     
45931     
45932     
45933 }
45934 /**
45935  * @class Roo.htmleditor.KeyEnter
45936  * Handle Enter press..
45937  * @cfg {Roo.HtmlEditorCore} core the editor.
45938  * @constructor
45939  * Create a new Filter.
45940  * @param {Object} config Configuration options
45941  */
45942
45943
45944
45945 Roo.htmleditor.KeyEnter = function(cfg) {
45946     Roo.apply(this, cfg);
45947     // this does not actually call walk as it's really just a abstract class
45948  
45949     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
45950 }
45951
45952
45953 Roo.htmleditor.KeyEnter.prototype = {
45954     
45955     core : false,
45956     
45957     keypress : function(e) {
45958         if (e.charCode != 13) {
45959             return true;
45960         }
45961         e.preventDefault();
45962         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
45963         var doc = this.core.doc;
45964         
45965         var docFragment = doc.createDocumentFragment();
45966     
45967         //add a new line
45968         var newEle = doc.createTextNode('\n');
45969         docFragment.appendChild(newEle);
45970     
45971     
45972         var range = this.core.win.getSelection().getRangeAt(0);
45973         var n = range.commonAncestorContainer ;
45974         while (n && n.nodeType != 1) {
45975             n  = n.parentNode;
45976         }
45977         var li = false;
45978         if (n && n.tagName == 'UL') {
45979             li = doc.createElement('LI');
45980             n.appendChild(li);
45981             
45982         }
45983         if (n && n.tagName == 'LI') {
45984             li = doc.createElement('LI');
45985             if (n.nextSibling) {
45986                 n.parentNode.insertBefore(li, n.firstSibling);
45987                 
45988             } else {
45989                 n.parentNode.appendChild(li);
45990             }
45991         }
45992         if (li) {   
45993             range = doc.createRange();
45994             range.setStartAfter(li);
45995             range.collapse(true);
45996         
45997             //make the cursor there
45998             var sel = this.core.win.getSelection();
45999             sel.removeAllRanges();
46000             sel.addRange(range);
46001             return false;
46002             
46003             
46004         }
46005         //add the br, or p, or something else
46006         newEle = doc.createElement('br');
46007         docFragment.appendChild(newEle);
46008     
46009         //make the br replace selection
46010         
46011         range.deleteContents();
46012         
46013         range.insertNode(docFragment);
46014     
46015         //create a new range
46016         range = doc.createRange();
46017         range.setStartAfter(newEle);
46018         range.collapse(true);
46019     
46020         //make the cursor there
46021         var sel = this.core.win.getSelection();
46022         sel.removeAllRanges();
46023         sel.addRange(range);
46024     
46025         return false;
46026          
46027     }
46028 };
46029      
46030 /**
46031  * @class Roo.htmleditor.Block
46032  * Base class for html editor blocks - do not use it directly .. extend it..
46033  * @cfg {DomElement} node The node to apply stuff to.
46034  * @cfg {String} friendly_name the name that appears in the context bar about this block
46035  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
46036  
46037  * @constructor
46038  * Create a new Filter.
46039  * @param {Object} config Configuration options
46040  */
46041
46042 Roo.htmleditor.Block  = function(cfg)
46043 {
46044     // do nothing .. should not be called really.
46045 }
46046
46047 Roo.htmleditor.Block.factory = function(node)
46048 {
46049     
46050     var id = Roo.get(node).id;
46051     if (typeof(Roo.htmleditor.Block.cache[id]) != 'undefined') {
46052         Roo.htmleditor.Block.cache[id].readElement();
46053         return Roo.htmleditor.Block.cache[id];
46054     }
46055     
46056     var cls = Roo.htmleditor['Block' + Roo.get(node).attr('data-block')];
46057     if (typeof(cls) == 'undefined') {
46058         Roo.log("OOps missing block : " + 'Block' + Roo.get(node).attr('data-block'));
46059         return false;
46060     }
46061     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
46062     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
46063 };
46064 // question goes here... do we need to clear out this cache sometimes?
46065 // or show we make it relivant to the htmleditor.
46066 Roo.htmleditor.Block.cache = {};
46067
46068 Roo.htmleditor.Block.prototype = {
46069     
46070     node : false,
46071     
46072      // used by context menu
46073     friendly_name : 'Image with caption',
46074     
46075     context : false,
46076     /**
46077      * Update a node with values from this object
46078      * @param {DomElement} node
46079      */
46080     updateElement : function(node)
46081     {
46082         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
46083     },
46084      /**
46085      * convert to plain HTML for calling insertAtCursor..
46086      */
46087     toHTML : function()
46088     {
46089         return Roo.DomHelper.markup(this.toObject());
46090     },
46091     /**
46092      * used by readEleemnt to extract data from a node
46093      * may need improving as it's pretty basic
46094      
46095      * @param {DomElement} node
46096      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
46097      * @param {String} attribute (use html - for contents, or style for using next param as style)
46098      * @param {String} style the style property - eg. text-align
46099      */
46100     getVal : function(node, tag, attr, style)
46101     {
46102         var n = node;
46103         if (tag !== true && n.tagName != tag.toUpperCase()) {
46104             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
46105             // but kiss for now.
46106             n = node.getElementsByTagName(tag).item(0);
46107         }
46108         if (attr == 'html') {
46109             return n.innerHTML;
46110         }
46111         if (attr == 'style') {
46112             return Roo.get(n).getStyle(style);
46113         }
46114         
46115         return Roo.get(n).attr(attr);
46116             
46117     },
46118     /**
46119      * create a DomHelper friendly object - for use with 
46120      * Roo.DomHelper.markup / overwrite / etc..
46121      * (override this)
46122      */
46123     toObject : function()
46124     {
46125         return {};
46126     },
46127       /**
46128      * Read a node that has a 'data-block' property - and extract the values from it.
46129      * @param {DomElement} node - the node
46130      */
46131     readElement : function(node)
46132     {
46133         
46134     } 
46135     
46136     
46137 };
46138
46139  
46140
46141 /**
46142  * @class Roo.htmleditor.BlockFigure
46143  * Block that has an image and a figcaption
46144  * @cfg {String} image_src the url for the image
46145  * @cfg {String} align (left|right) alignment for the block default left
46146  * @cfg {String} text_align (left|right) alignment for the text caption default left.
46147  * @cfg {String} caption the text to appear below  (and in the alt tag)
46148  * @cfg {String|number} image_width the width of the image number or %?
46149  * @cfg {String|number} image_height the height of the image number or %?
46150  * 
46151  * @constructor
46152  * Create a new Filter.
46153  * @param {Object} config Configuration options
46154  */
46155
46156 Roo.htmleditor.BlockFigure = function(cfg)
46157 {
46158     if (cfg.node) {
46159         this.readElement(cfg.node);
46160         this.updateElement(cfg.node);
46161     }
46162     Roo.apply(this, cfg);
46163 }
46164 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
46165  
46166     
46167     // setable values.
46168     image_src: '',
46169     
46170     align: 'left',
46171     caption : '',
46172     text_align: 'left',
46173     
46174     width : '46%',
46175     margin: '2%',
46176     
46177     // used by context menu
46178     friendly_name : 'Image with caption',
46179     
46180     context : { // ?? static really
46181         width : {
46182             title: "Width",
46183             width: 40
46184             // ?? number
46185         },
46186         margin : {
46187             title: "Margin",
46188             width: 40
46189             // ?? number
46190         },
46191         align: {
46192             title: "Align",
46193             opts : [[ "left"],[ "right"]],
46194             width : 80
46195             
46196         },
46197         text_align: {
46198             title: "Caption Align",
46199             opts : [ [ "left"],[ "right"],[ "center"]],
46200             width : 80
46201         },
46202         
46203        
46204         image_src : {
46205             title: "Src",
46206             width: 220
46207         }
46208     },
46209     /**
46210      * create a DomHelper friendly object - for use with
46211      * Roo.DomHelper.markup / overwrite / etc..
46212      */
46213     toObject : function()
46214     {
46215         var d = document.createElement('div');
46216         d.innerHTML = this.caption;
46217         
46218         return {
46219             tag: 'figure',
46220             'data-block' : 'Figure',
46221             contenteditable : 'false',
46222             style : {
46223                 display: 'table',
46224                 float :  this.align ,
46225                 width :  this.width,
46226                 margin:  this.margin
46227             },
46228             cn : [
46229                 {
46230                     tag : 'img',
46231                     src : this.image_src,
46232                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
46233                     style: {
46234                         width: '100%'
46235                     }
46236                 },
46237                 {
46238                     tag: 'figcaption',
46239                     contenteditable : true,
46240                     style : {
46241                         'text-align': this.text_align
46242                     },
46243                     html : this.caption
46244                     
46245                 }
46246             ]
46247         };
46248     },
46249     
46250     readElement : function(node)
46251     {
46252         this.image_src = this.getVal(node, 'img', 'src');
46253         this.align = this.getVal(node, 'figure', 'style', 'float');
46254         this.caption = this.getVal(node, 'figcaption', 'html');
46255         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
46256         this.width = this.getVal(node, 'figure', 'style', 'width');
46257         this.margin = this.getVal(node, 'figure', 'style', 'margin');
46258         
46259     } 
46260     
46261   
46262    
46263      
46264     
46265     
46266     
46267     
46268 })
46269
46270 //<script type="text/javascript">
46271
46272 /*
46273  * Based  Ext JS Library 1.1.1
46274  * Copyright(c) 2006-2007, Ext JS, LLC.
46275  * LGPL
46276  *
46277  */
46278  
46279 /**
46280  * @class Roo.HtmlEditorCore
46281  * @extends Roo.Component
46282  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
46283  *
46284  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46285  */
46286
46287 Roo.HtmlEditorCore = function(config){
46288     
46289     
46290     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
46291     
46292     
46293     this.addEvents({
46294         /**
46295          * @event initialize
46296          * Fires when the editor is fully initialized (including the iframe)
46297          * @param {Roo.HtmlEditorCore} this
46298          */
46299         initialize: true,
46300         /**
46301          * @event activate
46302          * Fires when the editor is first receives the focus. Any insertion must wait
46303          * until after this event.
46304          * @param {Roo.HtmlEditorCore} this
46305          */
46306         activate: true,
46307          /**
46308          * @event beforesync
46309          * Fires before the textarea is updated with content from the editor iframe. Return false
46310          * to cancel the sync.
46311          * @param {Roo.HtmlEditorCore} this
46312          * @param {String} html
46313          */
46314         beforesync: true,
46315          /**
46316          * @event beforepush
46317          * Fires before the iframe editor is updated with content from the textarea. Return false
46318          * to cancel the push.
46319          * @param {Roo.HtmlEditorCore} this
46320          * @param {String} html
46321          */
46322         beforepush: true,
46323          /**
46324          * @event sync
46325          * Fires when the textarea is updated with content from the editor iframe.
46326          * @param {Roo.HtmlEditorCore} this
46327          * @param {String} html
46328          */
46329         sync: true,
46330          /**
46331          * @event push
46332          * Fires when the iframe editor is updated with content from the textarea.
46333          * @param {Roo.HtmlEditorCore} this
46334          * @param {String} html
46335          */
46336         push: true,
46337         
46338         /**
46339          * @event editorevent
46340          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46341          * @param {Roo.HtmlEditorCore} this
46342          */
46343         editorevent: true
46344         
46345     });
46346     
46347     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
46348     
46349     // defaults : white / black...
46350     this.applyBlacklists();
46351     
46352     
46353     
46354 };
46355
46356
46357 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
46358
46359
46360      /**
46361      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
46362      */
46363     
46364     owner : false,
46365     
46366      /**
46367      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46368      *                        Roo.resizable.
46369      */
46370     resizable : false,
46371      /**
46372      * @cfg {Number} height (in pixels)
46373      */   
46374     height: 300,
46375    /**
46376      * @cfg {Number} width (in pixels)
46377      */   
46378     width: 500,
46379     
46380     /**
46381      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
46382      * 
46383      */
46384     stylesheets: false,
46385     
46386     /**
46387      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46388      */
46389     allowComments: false,
46390     // id of frame..
46391     frameId: false,
46392     
46393     // private properties
46394     validationEvent : false,
46395     deferHeight: true,
46396     initialized : false,
46397     activated : false,
46398     sourceEditMode : false,
46399     onFocus : Roo.emptyFn,
46400     iframePad:3,
46401     hideMode:'offsets',
46402     
46403     clearUp: true,
46404     
46405     // blacklist + whitelisted elements..
46406     black: false,
46407     white: false,
46408      
46409     bodyCls : '',
46410
46411     /**
46412      * Protected method that will not generally be called directly. It
46413      * is called when the editor initializes the iframe with HTML contents. Override this method if you
46414      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
46415      */
46416     getDocMarkup : function(){
46417         // body styles..
46418         var st = '';
46419         
46420         // inherit styels from page...?? 
46421         if (this.stylesheets === false) {
46422             
46423             Roo.get(document.head).select('style').each(function(node) {
46424                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46425             });
46426             
46427             Roo.get(document.head).select('link').each(function(node) { 
46428                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46429             });
46430             
46431         } else if (!this.stylesheets.length) {
46432                 // simple..
46433                 st = '<style type="text/css">' +
46434                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46435                    '</style>';
46436         } else {
46437             for (var i in this.stylesheets) {
46438                 if (typeof(this.stylesheets[i]) != 'string') {
46439                     continue;
46440                 }
46441                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
46442             }
46443             
46444         }
46445         
46446         st +=  '<style type="text/css">' +
46447             'IMG { cursor: pointer } ' +
46448         '</style>';
46449
46450         var cls = 'roo-htmleditor-body';
46451         
46452         if(this.bodyCls.length){
46453             cls += ' ' + this.bodyCls;
46454         }
46455         
46456         return '<html><head>' + st  +
46457             //<style type="text/css">' +
46458             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46459             //'</style>' +
46460             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
46461     },
46462
46463     // private
46464     onRender : function(ct, position)
46465     {
46466         var _t = this;
46467         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
46468         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
46469         
46470         
46471         this.el.dom.style.border = '0 none';
46472         this.el.dom.setAttribute('tabIndex', -1);
46473         this.el.addClass('x-hidden hide');
46474         
46475         
46476         
46477         if(Roo.isIE){ // fix IE 1px bogus margin
46478             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
46479         }
46480        
46481         
46482         this.frameId = Roo.id();
46483         
46484          
46485         
46486         var iframe = this.owner.wrap.createChild({
46487             tag: 'iframe',
46488             cls: 'form-control', // bootstrap..
46489             id: this.frameId,
46490             name: this.frameId,
46491             frameBorder : 'no',
46492             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
46493         }, this.el
46494         );
46495         
46496         
46497         this.iframe = iframe.dom;
46498
46499         this.assignDocWin();
46500         
46501         this.doc.designMode = 'on';
46502        
46503         this.doc.open();
46504         this.doc.write(this.getDocMarkup());
46505         this.doc.close();
46506
46507         
46508         var task = { // must defer to wait for browser to be ready
46509             run : function(){
46510                 //console.log("run task?" + this.doc.readyState);
46511                 this.assignDocWin();
46512                 if(this.doc.body || this.doc.readyState == 'complete'){
46513                     try {
46514                         this.doc.designMode="on";
46515                     } catch (e) {
46516                         return;
46517                     }
46518                     Roo.TaskMgr.stop(task);
46519                     this.initEditor.defer(10, this);
46520                 }
46521             },
46522             interval : 10,
46523             duration: 10000,
46524             scope: this
46525         };
46526         Roo.TaskMgr.start(task);
46527
46528     },
46529
46530     // private
46531     onResize : function(w, h)
46532     {
46533          Roo.log('resize: ' +w + ',' + h );
46534         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
46535         if(!this.iframe){
46536             return;
46537         }
46538         if(typeof w == 'number'){
46539             
46540             this.iframe.style.width = w + 'px';
46541         }
46542         if(typeof h == 'number'){
46543             
46544             this.iframe.style.height = h + 'px';
46545             if(this.doc){
46546                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
46547             }
46548         }
46549         
46550     },
46551
46552     /**
46553      * Toggles the editor between standard and source edit mode.
46554      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46555      */
46556     toggleSourceEdit : function(sourceEditMode){
46557         
46558         this.sourceEditMode = sourceEditMode === true;
46559         
46560         if(this.sourceEditMode){
46561  
46562             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
46563             
46564         }else{
46565             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
46566             //this.iframe.className = '';
46567             this.deferFocus();
46568         }
46569         //this.setSize(this.owner.wrap.getSize());
46570         //this.fireEvent('editmodechange', this, this.sourceEditMode);
46571     },
46572
46573     
46574   
46575
46576     /**
46577      * Protected method that will not generally be called directly. If you need/want
46578      * custom HTML cleanup, this is the method you should override.
46579      * @param {String} html The HTML to be cleaned
46580      * return {String} The cleaned HTML
46581      */
46582     cleanHtml : function(html){
46583         html = String(html);
46584         if(html.length > 5){
46585             if(Roo.isSafari){ // strip safari nonsense
46586                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
46587             }
46588         }
46589         if(html == '&nbsp;'){
46590             html = '';
46591         }
46592         return html;
46593     },
46594
46595     /**
46596      * HTML Editor -> Textarea
46597      * Protected method that will not generally be called directly. Syncs the contents
46598      * of the editor iframe with the textarea.
46599      */
46600     syncValue : function()
46601     {
46602         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
46603         if(this.initialized){
46604             var bd = (this.doc.body || this.doc.documentElement);
46605             //this.cleanUpPaste(); -- this is done else where and causes havoc..
46606             
46607             // not sure if this is really the place for this
46608             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
46609             // this has to update attributes that get duped.. like alt and caption..
46610             
46611             
46612             //Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46613             //     Roo.htmleditor.Block.factory(e);
46614             //},this);
46615             
46616             
46617             var div = document.createElement('div');
46618             div.innerHTML = bd.innerHTML;
46619             // remove content editable. (blocks)
46620             
46621            
46622             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
46623             //?? tidy?
46624             var html = div.innerHTML;
46625             if(Roo.isSafari){
46626                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
46627                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
46628                 if(m && m[1]){
46629                     html = '<div style="'+m[0]+'">' + html + '</div>';
46630                 }
46631             }
46632             html = this.cleanHtml(html);
46633             // fix up the special chars.. normaly like back quotes in word...
46634             // however we do not want to do this with chinese..
46635             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
46636                 
46637                 var cc = match.charCodeAt();
46638
46639                 // Get the character value, handling surrogate pairs
46640                 if (match.length == 2) {
46641                     // It's a surrogate pair, calculate the Unicode code point
46642                     var high = match.charCodeAt(0) - 0xD800;
46643                     var low  = match.charCodeAt(1) - 0xDC00;
46644                     cc = (high * 0x400) + low + 0x10000;
46645                 }  else if (
46646                     (cc >= 0x4E00 && cc < 0xA000 ) ||
46647                     (cc >= 0x3400 && cc < 0x4E00 ) ||
46648                     (cc >= 0xf900 && cc < 0xfb00 )
46649                 ) {
46650                         return match;
46651                 }  
46652          
46653                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
46654                 return "&#" + cc + ";";
46655                 
46656                 
46657             });
46658             
46659             
46660              
46661             if(this.owner.fireEvent('beforesync', this, html) !== false){
46662                 this.el.dom.value = html;
46663                 this.owner.fireEvent('sync', this, html);
46664             }
46665         }
46666     },
46667
46668     /**
46669      * TEXTAREA -> EDITABLE
46670      * Protected method that will not generally be called directly. Pushes the value of the textarea
46671      * into the iframe editor.
46672      */
46673     pushValue : function()
46674     {
46675         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
46676         if(this.initialized){
46677             var v = this.el.dom.value.trim();
46678             
46679             
46680             if(this.owner.fireEvent('beforepush', this, v) !== false){
46681                 var d = (this.doc.body || this.doc.documentElement);
46682                 d.innerHTML = v;
46683                  
46684                 this.el.dom.value = d.innerHTML;
46685                 this.owner.fireEvent('push', this, v);
46686             }
46687             
46688             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46689                 
46690                 Roo.htmleditor.Block.factory(e);
46691                 
46692             },this);
46693             var lc = this.doc.body.lastChild;
46694             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
46695                 // add an extra line at the end.
46696                 this.doc.body.appendChild(this.doc.createElement('br'));
46697             }
46698             
46699             
46700         }
46701     },
46702
46703     // private
46704     deferFocus : function(){
46705         this.focus.defer(10, this);
46706     },
46707
46708     // doc'ed in Field
46709     focus : function(){
46710         if(this.win && !this.sourceEditMode){
46711             this.win.focus();
46712         }else{
46713             this.el.focus();
46714         }
46715     },
46716     
46717     assignDocWin: function()
46718     {
46719         var iframe = this.iframe;
46720         
46721          if(Roo.isIE){
46722             this.doc = iframe.contentWindow.document;
46723             this.win = iframe.contentWindow;
46724         } else {
46725 //            if (!Roo.get(this.frameId)) {
46726 //                return;
46727 //            }
46728 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
46729 //            this.win = Roo.get(this.frameId).dom.contentWindow;
46730             
46731             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
46732                 return;
46733             }
46734             
46735             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
46736             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
46737         }
46738     },
46739     
46740     // private
46741     initEditor : function(){
46742         //console.log("INIT EDITOR");
46743         this.assignDocWin();
46744         
46745         
46746         
46747         this.doc.designMode="on";
46748         this.doc.open();
46749         this.doc.write(this.getDocMarkup());
46750         this.doc.close();
46751         
46752         var dbody = (this.doc.body || this.doc.documentElement);
46753         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
46754         // this copies styles from the containing element into thsi one..
46755         // not sure why we need all of this..
46756         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
46757         
46758         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
46759         //ss['background-attachment'] = 'fixed'; // w3c
46760         dbody.bgProperties = 'fixed'; // ie
46761         //Roo.DomHelper.applyStyles(dbody, ss);
46762         Roo.EventManager.on(this.doc, {
46763             //'mousedown': this.onEditorEvent,
46764             'mouseup': this.onEditorEvent,
46765             'dblclick': this.onEditorEvent,
46766             'click': this.onEditorEvent,
46767             'keyup': this.onEditorEvent,
46768             
46769             buffer:100,
46770             scope: this
46771         });
46772         Roo.EventManager.on(this.doc, {
46773             'paste': this.onPasteEvent,
46774             scope : this
46775         });
46776         if(Roo.isGecko){
46777             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
46778         }
46779         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
46780             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
46781         }
46782         this.initialized = true;
46783
46784         
46785         // initialize special key events - enter
46786         new Roo.htmleditor.KeyEnter({core : this});
46787         
46788          
46789         
46790         this.owner.fireEvent('initialize', this);
46791         this.pushValue();
46792     },
46793     
46794     onPasteEvent : function(e,v)
46795     {
46796         // I think we better assume paste is going to be a dirty load of rubish from word..
46797         
46798         // even pasting into a 'email version' of this widget will have to clean up that mess.
46799         var cd = (e.browserEvent.clipboardData || window.clipboardData);
46800         
46801         // check what type of paste - if it's an image, then handle it differently.
46802         if (cd.files.length > 0) {
46803             // pasting images?
46804             var urlAPI = (window.createObjectURL && window) || 
46805                 (window.URL && URL.revokeObjectURL && URL) || 
46806                 (window.webkitURL && webkitURL);
46807     
46808             var url = urlAPI.createObjectURL( cd.files[0]);
46809             this.insertAtCursor('<img src=" + url + ">');
46810             return false;
46811         }
46812         
46813         var html = cd.getData('text/html'); // clipboard event
46814         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
46815         var images = parser.doc.getElementsByType('pict');
46816         Roo.log(images);
46817         //Roo.log(imgs);
46818         // fixme..
46819         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
46820                        .map(function(g) { return g.toDataURL(); });
46821         
46822         
46823         html = this.cleanWordChars(html);
46824         
46825         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
46826         
46827         if (images.length > 0) {
46828             Roo.each(d.getElementsByTagName('img'), function(img, i) {
46829                 img.setAttribute('src', images[i]);
46830             });
46831         }
46832         
46833       
46834         new Roo.htmleditor.FilterStyleToTag({ node : d });
46835         new Roo.htmleditor.FilterAttributes({
46836             node : d,
46837             attrib_white : ['href', 'src', 'name', 'align'],
46838             attrib_clean : ['href', 'src' ] 
46839         });
46840         new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
46841         // should be fonts..
46842         new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
46843         new Roo.htmleditor.FilterParagraph({ node : d });
46844         new Roo.htmleditor.FilterSpan({ node : d });
46845         new Roo.htmleditor.FilterLongBr({ node : d });
46846         
46847         
46848         
46849         this.insertAtCursor(d.innerHTML);
46850         
46851         e.preventDefault();
46852         return false;
46853         // default behaveiour should be our local cleanup paste? (optional?)
46854         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
46855         //this.owner.fireEvent('paste', e, v);
46856     },
46857     // private
46858     onDestroy : function(){
46859         
46860         
46861         
46862         if(this.rendered){
46863             
46864             //for (var i =0; i < this.toolbars.length;i++) {
46865             //    // fixme - ask toolbars for heights?
46866             //    this.toolbars[i].onDestroy();
46867            // }
46868             
46869             //this.wrap.dom.innerHTML = '';
46870             //this.wrap.remove();
46871         }
46872     },
46873
46874     // private
46875     onFirstFocus : function(){
46876         
46877         this.assignDocWin();
46878         
46879         
46880         this.activated = true;
46881          
46882     
46883         if(Roo.isGecko){ // prevent silly gecko errors
46884             this.win.focus();
46885             var s = this.win.getSelection();
46886             if(!s.focusNode || s.focusNode.nodeType != 3){
46887                 var r = s.getRangeAt(0);
46888                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
46889                 r.collapse(true);
46890                 this.deferFocus();
46891             }
46892             try{
46893                 this.execCmd('useCSS', true);
46894                 this.execCmd('styleWithCSS', false);
46895             }catch(e){}
46896         }
46897         this.owner.fireEvent('activate', this);
46898     },
46899
46900     // private
46901     adjustFont: function(btn){
46902         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
46903         //if(Roo.isSafari){ // safari
46904         //    adjust *= 2;
46905        // }
46906         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
46907         if(Roo.isSafari){ // safari
46908             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
46909             v =  (v < 10) ? 10 : v;
46910             v =  (v > 48) ? 48 : v;
46911             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
46912             
46913         }
46914         
46915         
46916         v = Math.max(1, v+adjust);
46917         
46918         this.execCmd('FontSize', v  );
46919     },
46920
46921     onEditorEvent : function(e)
46922     {
46923         this.owner.fireEvent('editorevent', this, e);
46924       //  this.updateToolbar();
46925         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
46926     },
46927
46928     insertTag : function(tg)
46929     {
46930         // could be a bit smarter... -> wrap the current selected tRoo..
46931         if (tg.toLowerCase() == 'span' ||
46932             tg.toLowerCase() == 'code' ||
46933             tg.toLowerCase() == 'sup' ||
46934             tg.toLowerCase() == 'sub' 
46935             ) {
46936             
46937             range = this.createRange(this.getSelection());
46938             var wrappingNode = this.doc.createElement(tg.toLowerCase());
46939             wrappingNode.appendChild(range.extractContents());
46940             range.insertNode(wrappingNode);
46941
46942             return;
46943             
46944             
46945             
46946         }
46947         this.execCmd("formatblock",   tg);
46948         
46949     },
46950     
46951     insertText : function(txt)
46952     {
46953         
46954         
46955         var range = this.createRange();
46956         range.deleteContents();
46957                //alert(Sender.getAttribute('label'));
46958                
46959         range.insertNode(this.doc.createTextNode(txt));
46960     } ,
46961     
46962      
46963
46964     /**
46965      * Executes a Midas editor command on the editor document and performs necessary focus and
46966      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
46967      * @param {String} cmd The Midas command
46968      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
46969      */
46970     relayCmd : function(cmd, value){
46971         this.win.focus();
46972         this.execCmd(cmd, value);
46973         this.owner.fireEvent('editorevent', this);
46974         //this.updateToolbar();
46975         this.owner.deferFocus();
46976     },
46977
46978     /**
46979      * Executes a Midas editor command directly on the editor document.
46980      * For visual commands, you should use {@link #relayCmd} instead.
46981      * <b>This should only be called after the editor is initialized.</b>
46982      * @param {String} cmd The Midas command
46983      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
46984      */
46985     execCmd : function(cmd, value){
46986         this.doc.execCommand(cmd, false, value === undefined ? null : value);
46987         this.syncValue();
46988     },
46989  
46990  
46991    
46992     /**
46993      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
46994      * to insert tRoo.
46995      * @param {String} text | dom node.. 
46996      */
46997     insertAtCursor : function(text)
46998     {
46999         
47000         if(!this.activated){
47001             return;
47002         }
47003          
47004         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
47005             this.win.focus();
47006             
47007             
47008             // from jquery ui (MIT licenced)
47009             var range, node;
47010             var win = this.win;
47011             
47012             if (win.getSelection && win.getSelection().getRangeAt) {
47013                 
47014                 // delete the existing?
47015                 
47016                 this.createRange(this.getSelection()).deleteContents();
47017                 range = win.getSelection().getRangeAt(0);
47018                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
47019                 range.insertNode(node);
47020             } else if (win.document.selection && win.document.selection.createRange) {
47021                 // no firefox support
47022                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47023                 win.document.selection.createRange().pasteHTML(txt);
47024             } else {
47025                 // no firefox support
47026                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47027                 this.execCmd('InsertHTML', txt);
47028             } 
47029             
47030             this.syncValue();
47031             
47032             this.deferFocus();
47033         }
47034     },
47035  // private
47036     mozKeyPress : function(e){
47037         if(e.ctrlKey){
47038             var c = e.getCharCode(), cmd;
47039           
47040             if(c > 0){
47041                 c = String.fromCharCode(c).toLowerCase();
47042                 switch(c){
47043                     case 'b':
47044                         cmd = 'bold';
47045                         break;
47046                     case 'i':
47047                         cmd = 'italic';
47048                         break;
47049                     
47050                     case 'u':
47051                         cmd = 'underline';
47052                         break;
47053                     
47054                     //case 'v':
47055                       //  this.cleanUpPaste.defer(100, this);
47056                       //  return;
47057                         
47058                 }
47059                 if(cmd){
47060                     this.win.focus();
47061                     this.execCmd(cmd);
47062                     this.deferFocus();
47063                     e.preventDefault();
47064                 }
47065                 
47066             }
47067         }
47068     },
47069
47070     // private
47071     fixKeys : function(){ // load time branching for fastest keydown performance
47072         if(Roo.isIE){
47073             return function(e){
47074                 var k = e.getKey(), r;
47075                 if(k == e.TAB){
47076                     e.stopEvent();
47077                     r = this.doc.selection.createRange();
47078                     if(r){
47079                         r.collapse(true);
47080                         r.pasteHTML('&#160;&#160;&#160;&#160;');
47081                         this.deferFocus();
47082                     }
47083                     return;
47084                 }
47085                 
47086                 if(k == e.ENTER){
47087                     r = this.doc.selection.createRange();
47088                     if(r){
47089                         var target = r.parentElement();
47090                         if(!target || target.tagName.toLowerCase() != 'li'){
47091                             e.stopEvent();
47092                             r.pasteHTML('<br/>');
47093                             r.collapse(false);
47094                             r.select();
47095                         }
47096                     }
47097                 }
47098                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47099                 //    this.cleanUpPaste.defer(100, this);
47100                 //    return;
47101                 //}
47102                 
47103                 
47104             };
47105         }else if(Roo.isOpera){
47106             return function(e){
47107                 var k = e.getKey();
47108                 if(k == e.TAB){
47109                     e.stopEvent();
47110                     this.win.focus();
47111                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
47112                     this.deferFocus();
47113                 }
47114                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47115                 //    this.cleanUpPaste.defer(100, this);
47116                  //   return;
47117                 //}
47118                 
47119             };
47120         }else if(Roo.isSafari){
47121             return function(e){
47122                 var k = e.getKey();
47123                 
47124                 if(k == e.TAB){
47125                     e.stopEvent();
47126                     this.execCmd('InsertText','\t');
47127                     this.deferFocus();
47128                     return;
47129                 }
47130                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47131                  //   this.cleanUpPaste.defer(100, this);
47132                  //   return;
47133                // }
47134                 
47135              };
47136         }
47137     }(),
47138     
47139     getAllAncestors: function()
47140     {
47141         var p = this.getSelectedNode();
47142         var a = [];
47143         if (!p) {
47144             a.push(p); // push blank onto stack..
47145             p = this.getParentElement();
47146         }
47147         
47148         
47149         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
47150             a.push(p);
47151             p = p.parentNode;
47152         }
47153         a.push(this.doc.body);
47154         return a;
47155     },
47156     lastSel : false,
47157     lastSelNode : false,
47158     
47159     
47160     getSelection : function() 
47161     {
47162         this.assignDocWin();
47163         return Roo.isIE ? this.doc.selection : this.win.getSelection();
47164     },
47165     /**
47166      * Select a dom node
47167      * @param {DomElement} node the node to select
47168      */
47169     selectNode : function(node)
47170     {
47171         
47172             var nodeRange = node.ownerDocument.createRange();
47173             try {
47174                 nodeRange.selectNode(node);
47175             } catch (e) {
47176                 nodeRange.selectNodeContents(node);
47177             }
47178             //nodeRange.collapse(true);
47179             var s = this.win.getSelection();
47180             s.removeAllRanges();
47181             s.addRange(nodeRange);
47182     },
47183     
47184     getSelectedNode: function() 
47185     {
47186         // this may only work on Gecko!!!
47187         
47188         // should we cache this!!!!
47189         
47190         
47191         
47192          
47193         var range = this.createRange(this.getSelection()).cloneRange();
47194         
47195         if (Roo.isIE) {
47196             var parent = range.parentElement();
47197             while (true) {
47198                 var testRange = range.duplicate();
47199                 testRange.moveToElementText(parent);
47200                 if (testRange.inRange(range)) {
47201                     break;
47202                 }
47203                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
47204                     break;
47205                 }
47206                 parent = parent.parentElement;
47207             }
47208             return parent;
47209         }
47210         
47211         // is ancestor a text element.
47212         var ac =  range.commonAncestorContainer;
47213         if (ac.nodeType == 3) {
47214             ac = ac.parentNode;
47215         }
47216         
47217         var ar = ac.childNodes;
47218          
47219         var nodes = [];
47220         var other_nodes = [];
47221         var has_other_nodes = false;
47222         for (var i=0;i<ar.length;i++) {
47223             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
47224                 continue;
47225             }
47226             // fullly contained node.
47227             
47228             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
47229                 nodes.push(ar[i]);
47230                 continue;
47231             }
47232             
47233             // probably selected..
47234             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
47235                 other_nodes.push(ar[i]);
47236                 continue;
47237             }
47238             // outer..
47239             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
47240                 continue;
47241             }
47242             
47243             
47244             has_other_nodes = true;
47245         }
47246         if (!nodes.length && other_nodes.length) {
47247             nodes= other_nodes;
47248         }
47249         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
47250             return false;
47251         }
47252         
47253         return nodes[0];
47254     },
47255     createRange: function(sel)
47256     {
47257         // this has strange effects when using with 
47258         // top toolbar - not sure if it's a great idea.
47259         //this.editor.contentWindow.focus();
47260         if (typeof sel != "undefined") {
47261             try {
47262                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
47263             } catch(e) {
47264                 return this.doc.createRange();
47265             }
47266         } else {
47267             return this.doc.createRange();
47268         }
47269     },
47270     getParentElement: function()
47271     {
47272         
47273         this.assignDocWin();
47274         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
47275         
47276         var range = this.createRange(sel);
47277          
47278         try {
47279             var p = range.commonAncestorContainer;
47280             while (p.nodeType == 3) { // text node
47281                 p = p.parentNode;
47282             }
47283             return p;
47284         } catch (e) {
47285             return null;
47286         }
47287     
47288     },
47289     /***
47290      *
47291      * Range intersection.. the hard stuff...
47292      *  '-1' = before
47293      *  '0' = hits..
47294      *  '1' = after.
47295      *         [ -- selected range --- ]
47296      *   [fail]                        [fail]
47297      *
47298      *    basically..
47299      *      if end is before start or  hits it. fail.
47300      *      if start is after end or hits it fail.
47301      *
47302      *   if either hits (but other is outside. - then it's not 
47303      *   
47304      *    
47305      **/
47306     
47307     
47308     // @see http://www.thismuchiknow.co.uk/?p=64.
47309     rangeIntersectsNode : function(range, node)
47310     {
47311         var nodeRange = node.ownerDocument.createRange();
47312         try {
47313             nodeRange.selectNode(node);
47314         } catch (e) {
47315             nodeRange.selectNodeContents(node);
47316         }
47317     
47318         var rangeStartRange = range.cloneRange();
47319         rangeStartRange.collapse(true);
47320     
47321         var rangeEndRange = range.cloneRange();
47322         rangeEndRange.collapse(false);
47323     
47324         var nodeStartRange = nodeRange.cloneRange();
47325         nodeStartRange.collapse(true);
47326     
47327         var nodeEndRange = nodeRange.cloneRange();
47328         nodeEndRange.collapse(false);
47329     
47330         return rangeStartRange.compareBoundaryPoints(
47331                  Range.START_TO_START, nodeEndRange) == -1 &&
47332                rangeEndRange.compareBoundaryPoints(
47333                  Range.START_TO_START, nodeStartRange) == 1;
47334         
47335          
47336     },
47337     rangeCompareNode : function(range, node)
47338     {
47339         var nodeRange = node.ownerDocument.createRange();
47340         try {
47341             nodeRange.selectNode(node);
47342         } catch (e) {
47343             nodeRange.selectNodeContents(node);
47344         }
47345         
47346         
47347         range.collapse(true);
47348     
47349         nodeRange.collapse(true);
47350      
47351         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
47352         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
47353          
47354         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
47355         
47356         var nodeIsBefore   =  ss == 1;
47357         var nodeIsAfter    = ee == -1;
47358         
47359         if (nodeIsBefore && nodeIsAfter) {
47360             return 0; // outer
47361         }
47362         if (!nodeIsBefore && nodeIsAfter) {
47363             return 1; //right trailed.
47364         }
47365         
47366         if (nodeIsBefore && !nodeIsAfter) {
47367             return 2;  // left trailed.
47368         }
47369         // fully contined.
47370         return 3;
47371     },
47372  
47373     cleanWordChars : function(input) {// change the chars to hex code
47374         
47375        var swapCodes  = [ 
47376             [    8211, "&#8211;" ], 
47377             [    8212, "&#8212;" ], 
47378             [    8216,  "'" ],  
47379             [    8217, "'" ],  
47380             [    8220, '"' ],  
47381             [    8221, '"' ],  
47382             [    8226, "*" ],  
47383             [    8230, "..." ]
47384         ]; 
47385         var output = input;
47386         Roo.each(swapCodes, function(sw) { 
47387             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
47388             
47389             output = output.replace(swapper, sw[1]);
47390         });
47391         
47392         return output;
47393     },
47394     
47395      
47396     
47397         
47398     
47399     cleanUpChild : function (node)
47400     {
47401         
47402         new Roo.htmleditor.FilterComment({node : node});
47403         new Roo.htmleditor.FilterAttributes({
47404                 node : node,
47405                 attrib_black : this.ablack,
47406                 attrib_clean : this.aclean,
47407                 style_white : this.cwhite,
47408                 style_black : this.cblack
47409         });
47410         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
47411         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
47412          
47413         
47414     },
47415     
47416     /**
47417      * Clean up MS wordisms...
47418      * @deprecated - use filter directly
47419      */
47420     cleanWord : function(node)
47421     {
47422         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
47423         
47424     },
47425    
47426     
47427     /**
47428
47429      * @deprecated - use filters
47430      */
47431     cleanTableWidths : function(node)
47432     {
47433         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
47434         
47435  
47436     },
47437     
47438      
47439         
47440     applyBlacklists : function()
47441     {
47442         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
47443         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
47444         
47445         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
47446         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
47447         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
47448         
47449         this.white = [];
47450         this.black = [];
47451         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
47452             if (b.indexOf(tag) > -1) {
47453                 return;
47454             }
47455             this.white.push(tag);
47456             
47457         }, this);
47458         
47459         Roo.each(w, function(tag) {
47460             if (b.indexOf(tag) > -1) {
47461                 return;
47462             }
47463             if (this.white.indexOf(tag) > -1) {
47464                 return;
47465             }
47466             this.white.push(tag);
47467             
47468         }, this);
47469         
47470         
47471         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
47472             if (w.indexOf(tag) > -1) {
47473                 return;
47474             }
47475             this.black.push(tag);
47476             
47477         }, this);
47478         
47479         Roo.each(b, function(tag) {
47480             if (w.indexOf(tag) > -1) {
47481                 return;
47482             }
47483             if (this.black.indexOf(tag) > -1) {
47484                 return;
47485             }
47486             this.black.push(tag);
47487             
47488         }, this);
47489         
47490         
47491         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
47492         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
47493         
47494         this.cwhite = [];
47495         this.cblack = [];
47496         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
47497             if (b.indexOf(tag) > -1) {
47498                 return;
47499             }
47500             this.cwhite.push(tag);
47501             
47502         }, this);
47503         
47504         Roo.each(w, function(tag) {
47505             if (b.indexOf(tag) > -1) {
47506                 return;
47507             }
47508             if (this.cwhite.indexOf(tag) > -1) {
47509                 return;
47510             }
47511             this.cwhite.push(tag);
47512             
47513         }, this);
47514         
47515         
47516         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
47517             if (w.indexOf(tag) > -1) {
47518                 return;
47519             }
47520             this.cblack.push(tag);
47521             
47522         }, this);
47523         
47524         Roo.each(b, function(tag) {
47525             if (w.indexOf(tag) > -1) {
47526                 return;
47527             }
47528             if (this.cblack.indexOf(tag) > -1) {
47529                 return;
47530             }
47531             this.cblack.push(tag);
47532             
47533         }, this);
47534     },
47535     
47536     setStylesheets : function(stylesheets)
47537     {
47538         if(typeof(stylesheets) == 'string'){
47539             Roo.get(this.iframe.contentDocument.head).createChild({
47540                 tag : 'link',
47541                 rel : 'stylesheet',
47542                 type : 'text/css',
47543                 href : stylesheets
47544             });
47545             
47546             return;
47547         }
47548         var _this = this;
47549      
47550         Roo.each(stylesheets, function(s) {
47551             if(!s.length){
47552                 return;
47553             }
47554             
47555             Roo.get(_this.iframe.contentDocument.head).createChild({
47556                 tag : 'link',
47557                 rel : 'stylesheet',
47558                 type : 'text/css',
47559                 href : s
47560             });
47561         });
47562
47563         
47564     },
47565     
47566     removeStylesheets : function()
47567     {
47568         var _this = this;
47569         
47570         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
47571             s.remove();
47572         });
47573     },
47574     
47575     setStyle : function(style)
47576     {
47577         Roo.get(this.iframe.contentDocument.head).createChild({
47578             tag : 'style',
47579             type : 'text/css',
47580             html : style
47581         });
47582
47583         return;
47584     }
47585     
47586     // hide stuff that is not compatible
47587     /**
47588      * @event blur
47589      * @hide
47590      */
47591     /**
47592      * @event change
47593      * @hide
47594      */
47595     /**
47596      * @event focus
47597      * @hide
47598      */
47599     /**
47600      * @event specialkey
47601      * @hide
47602      */
47603     /**
47604      * @cfg {String} fieldClass @hide
47605      */
47606     /**
47607      * @cfg {String} focusClass @hide
47608      */
47609     /**
47610      * @cfg {String} autoCreate @hide
47611      */
47612     /**
47613      * @cfg {String} inputType @hide
47614      */
47615     /**
47616      * @cfg {String} invalidClass @hide
47617      */
47618     /**
47619      * @cfg {String} invalidText @hide
47620      */
47621     /**
47622      * @cfg {String} msgFx @hide
47623      */
47624     /**
47625      * @cfg {String} validateOnBlur @hide
47626      */
47627 });
47628
47629 Roo.HtmlEditorCore.white = [
47630         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
47631         
47632        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
47633        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
47634        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
47635        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
47636        'TABLE',   'UL',         'XMP', 
47637        
47638        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
47639       'THEAD',   'TR', 
47640      
47641       'DIR', 'MENU', 'OL', 'UL', 'DL',
47642        
47643       'EMBED',  'OBJECT'
47644 ];
47645
47646
47647 Roo.HtmlEditorCore.black = [
47648     //    'embed',  'object', // enable - backend responsiblity to clean thiese
47649         'APPLET', // 
47650         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
47651         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
47652         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
47653         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
47654         //'FONT' // CLEAN LATER..
47655         'COLGROUP', 'COL'  // messy tables.
47656         
47657 ];
47658 Roo.HtmlEditorCore.clean = [ // ?? needed???
47659      'SCRIPT', 'STYLE', 'TITLE', 'XML'
47660 ];
47661 Roo.HtmlEditorCore.tag_remove = [
47662     'FONT', 'TBODY'  
47663 ];
47664 // attributes..
47665
47666 Roo.HtmlEditorCore.ablack = [
47667     'on'
47668 ];
47669     
47670 Roo.HtmlEditorCore.aclean = [ 
47671     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
47672 ];
47673
47674 // protocols..
47675 Roo.HtmlEditorCore.pwhite= [
47676         'http',  'https',  'mailto'
47677 ];
47678
47679 // white listed style attributes.
47680 Roo.HtmlEditorCore.cwhite= [
47681       //  'text-align', /// default is to allow most things..
47682       
47683          
47684 //        'font-size'//??
47685 ];
47686
47687 // black listed style attributes.
47688 Roo.HtmlEditorCore.cblack= [
47689       //  'font-size' -- this can be set by the project 
47690 ];
47691
47692
47693
47694
47695     //<script type="text/javascript">
47696
47697 /*
47698  * Ext JS Library 1.1.1
47699  * Copyright(c) 2006-2007, Ext JS, LLC.
47700  * Licence LGPL
47701  * 
47702  */
47703  
47704  
47705 Roo.form.HtmlEditor = function(config){
47706     
47707     
47708     
47709     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
47710     
47711     if (!this.toolbars) {
47712         this.toolbars = [];
47713     }
47714     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
47715     
47716     
47717 };
47718
47719 /**
47720  * @class Roo.form.HtmlEditor
47721  * @extends Roo.form.Field
47722  * Provides a lightweight HTML Editor component.
47723  *
47724  * This has been tested on Fireforx / Chrome.. IE may not be so great..
47725  * 
47726  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
47727  * supported by this editor.</b><br/><br/>
47728  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
47729  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
47730  */
47731 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
47732     /**
47733      * @cfg {Boolean} clearUp
47734      */
47735     clearUp : true,
47736       /**
47737      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
47738      */
47739     toolbars : false,
47740    
47741      /**
47742      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
47743      *                        Roo.resizable.
47744      */
47745     resizable : false,
47746      /**
47747      * @cfg {Number} height (in pixels)
47748      */   
47749     height: 300,
47750    /**
47751      * @cfg {Number} width (in pixels)
47752      */   
47753     width: 500,
47754     
47755     /**
47756      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
47757      * 
47758      */
47759     stylesheets: false,
47760     
47761     
47762      /**
47763      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
47764      * 
47765      */
47766     cblack: false,
47767     /**
47768      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
47769      * 
47770      */
47771     cwhite: false,
47772     
47773      /**
47774      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
47775      * 
47776      */
47777     black: false,
47778     /**
47779      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
47780      * 
47781      */
47782     white: false,
47783     /**
47784      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
47785      */
47786     allowComments: false,
47787     /**
47788      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
47789      */
47790     
47791     
47792      bodyCls : '',
47793     
47794     // id of frame..
47795     frameId: false,
47796     
47797     // private properties
47798     validationEvent : false,
47799     deferHeight: true,
47800     initialized : false,
47801     activated : false,
47802     
47803     onFocus : Roo.emptyFn,
47804     iframePad:3,
47805     hideMode:'offsets',
47806     
47807     actionMode : 'container', // defaults to hiding it...
47808     
47809     defaultAutoCreate : { // modified by initCompnoent..
47810         tag: "textarea",
47811         style:"width:500px;height:300px;",
47812         autocomplete: "new-password"
47813     },
47814
47815     // private
47816     initComponent : function(){
47817         this.addEvents({
47818             /**
47819              * @event initialize
47820              * Fires when the editor is fully initialized (including the iframe)
47821              * @param {HtmlEditor} this
47822              */
47823             initialize: true,
47824             /**
47825              * @event activate
47826              * Fires when the editor is first receives the focus. Any insertion must wait
47827              * until after this event.
47828              * @param {HtmlEditor} this
47829              */
47830             activate: true,
47831              /**
47832              * @event beforesync
47833              * Fires before the textarea is updated with content from the editor iframe. Return false
47834              * to cancel the sync.
47835              * @param {HtmlEditor} this
47836              * @param {String} html
47837              */
47838             beforesync: true,
47839              /**
47840              * @event beforepush
47841              * Fires before the iframe editor is updated with content from the textarea. Return false
47842              * to cancel the push.
47843              * @param {HtmlEditor} this
47844              * @param {String} html
47845              */
47846             beforepush: true,
47847              /**
47848              * @event sync
47849              * Fires when the textarea is updated with content from the editor iframe.
47850              * @param {HtmlEditor} this
47851              * @param {String} html
47852              */
47853             sync: true,
47854              /**
47855              * @event push
47856              * Fires when the iframe editor is updated with content from the textarea.
47857              * @param {HtmlEditor} this
47858              * @param {String} html
47859              */
47860             push: true,
47861              /**
47862              * @event editmodechange
47863              * Fires when the editor switches edit modes
47864              * @param {HtmlEditor} this
47865              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
47866              */
47867             editmodechange: true,
47868             /**
47869              * @event editorevent
47870              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
47871              * @param {HtmlEditor} this
47872              */
47873             editorevent: true,
47874             /**
47875              * @event firstfocus
47876              * Fires when on first focus - needed by toolbars..
47877              * @param {HtmlEditor} this
47878              */
47879             firstfocus: true,
47880             /**
47881              * @event autosave
47882              * Auto save the htmlEditor value as a file into Events
47883              * @param {HtmlEditor} this
47884              */
47885             autosave: true,
47886             /**
47887              * @event savedpreview
47888              * preview the saved version of htmlEditor
47889              * @param {HtmlEditor} this
47890              */
47891             savedpreview: true,
47892             
47893             /**
47894             * @event stylesheetsclick
47895             * Fires when press the Sytlesheets button
47896             * @param {Roo.HtmlEditorCore} this
47897             */
47898             stylesheetsclick: true,
47899             /**
47900             * @event paste
47901             * Fires when press user pastes into the editor
47902             * @param {Roo.HtmlEditorCore} this
47903             */
47904             paste: true 
47905         });
47906         this.defaultAutoCreate =  {
47907             tag: "textarea",
47908             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
47909             autocomplete: "new-password"
47910         };
47911     },
47912
47913     /**
47914      * Protected method that will not generally be called directly. It
47915      * is called when the editor creates its toolbar. Override this method if you need to
47916      * add custom toolbar buttons.
47917      * @param {HtmlEditor} editor
47918      */
47919     createToolbar : function(editor){
47920         Roo.log("create toolbars");
47921         if (!editor.toolbars || !editor.toolbars.length) {
47922             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
47923         }
47924         
47925         for (var i =0 ; i < editor.toolbars.length;i++) {
47926             editor.toolbars[i] = Roo.factory(
47927                     typeof(editor.toolbars[i]) == 'string' ?
47928                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
47929                 Roo.form.HtmlEditor);
47930             editor.toolbars[i].init(editor);
47931         }
47932          
47933         
47934     },
47935
47936      
47937     // private
47938     onRender : function(ct, position)
47939     {
47940         var _t = this;
47941         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
47942         
47943         this.wrap = this.el.wrap({
47944             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
47945         });
47946         
47947         this.editorcore.onRender(ct, position);
47948          
47949         if (this.resizable) {
47950             this.resizeEl = new Roo.Resizable(this.wrap, {
47951                 pinned : true,
47952                 wrap: true,
47953                 dynamic : true,
47954                 minHeight : this.height,
47955                 height: this.height,
47956                 handles : this.resizable,
47957                 width: this.width,
47958                 listeners : {
47959                     resize : function(r, w, h) {
47960                         _t.onResize(w,h); // -something
47961                     }
47962                 }
47963             });
47964             
47965         }
47966         this.createToolbar(this);
47967        
47968         
47969         if(!this.width){
47970             this.setSize(this.wrap.getSize());
47971         }
47972         if (this.resizeEl) {
47973             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
47974             // should trigger onReize..
47975         }
47976         
47977         this.keyNav = new Roo.KeyNav(this.el, {
47978             
47979             "tab" : function(e){
47980                 e.preventDefault();
47981                 
47982                 var value = this.getValue();
47983                 
47984                 var start = this.el.dom.selectionStart;
47985                 var end = this.el.dom.selectionEnd;
47986                 
47987                 if(!e.shiftKey){
47988                     
47989                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
47990                     this.el.dom.setSelectionRange(end + 1, end + 1);
47991                     return;
47992                 }
47993                 
47994                 var f = value.substring(0, start).split("\t");
47995                 
47996                 if(f.pop().length != 0){
47997                     return;
47998                 }
47999                 
48000                 this.setValue(f.join("\t") + value.substring(end));
48001                 this.el.dom.setSelectionRange(start - 1, start - 1);
48002                 
48003             },
48004             
48005             "home" : function(e){
48006                 e.preventDefault();
48007                 
48008                 var curr = this.el.dom.selectionStart;
48009                 var lines = this.getValue().split("\n");
48010                 
48011                 if(!lines.length){
48012                     return;
48013                 }
48014                 
48015                 if(e.ctrlKey){
48016                     this.el.dom.setSelectionRange(0, 0);
48017                     return;
48018                 }
48019                 
48020                 var pos = 0;
48021                 
48022                 for (var i = 0; i < lines.length;i++) {
48023                     pos += lines[i].length;
48024                     
48025                     if(i != 0){
48026                         pos += 1;
48027                     }
48028                     
48029                     if(pos < curr){
48030                         continue;
48031                     }
48032                     
48033                     pos -= lines[i].length;
48034                     
48035                     break;
48036                 }
48037                 
48038                 if(!e.shiftKey){
48039                     this.el.dom.setSelectionRange(pos, pos);
48040                     return;
48041                 }
48042                 
48043                 this.el.dom.selectionStart = pos;
48044                 this.el.dom.selectionEnd = curr;
48045             },
48046             
48047             "end" : function(e){
48048                 e.preventDefault();
48049                 
48050                 var curr = this.el.dom.selectionStart;
48051                 var lines = this.getValue().split("\n");
48052                 
48053                 if(!lines.length){
48054                     return;
48055                 }
48056                 
48057                 if(e.ctrlKey){
48058                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
48059                     return;
48060                 }
48061                 
48062                 var pos = 0;
48063                 
48064                 for (var i = 0; i < lines.length;i++) {
48065                     
48066                     pos += lines[i].length;
48067                     
48068                     if(i != 0){
48069                         pos += 1;
48070                     }
48071                     
48072                     if(pos < curr){
48073                         continue;
48074                     }
48075                     
48076                     break;
48077                 }
48078                 
48079                 if(!e.shiftKey){
48080                     this.el.dom.setSelectionRange(pos, pos);
48081                     return;
48082                 }
48083                 
48084                 this.el.dom.selectionStart = curr;
48085                 this.el.dom.selectionEnd = pos;
48086             },
48087
48088             scope : this,
48089
48090             doRelay : function(foo, bar, hname){
48091                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48092             },
48093
48094             forceKeyDown: true
48095         });
48096         
48097 //        if(this.autosave && this.w){
48098 //            this.autoSaveFn = setInterval(this.autosave, 1000);
48099 //        }
48100     },
48101
48102     // private
48103     onResize : function(w, h)
48104     {
48105         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
48106         var ew = false;
48107         var eh = false;
48108         
48109         if(this.el ){
48110             if(typeof w == 'number'){
48111                 var aw = w - this.wrap.getFrameWidth('lr');
48112                 this.el.setWidth(this.adjustWidth('textarea', aw));
48113                 ew = aw;
48114             }
48115             if(typeof h == 'number'){
48116                 var tbh = 0;
48117                 for (var i =0; i < this.toolbars.length;i++) {
48118                     // fixme - ask toolbars for heights?
48119                     tbh += this.toolbars[i].tb.el.getHeight();
48120                     if (this.toolbars[i].footer) {
48121                         tbh += this.toolbars[i].footer.el.getHeight();
48122                     }
48123                 }
48124                 
48125                 
48126                 
48127                 
48128                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
48129                 ah -= 5; // knock a few pixes off for look..
48130 //                Roo.log(ah);
48131                 this.el.setHeight(this.adjustWidth('textarea', ah));
48132                 var eh = ah;
48133             }
48134         }
48135         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
48136         this.editorcore.onResize(ew,eh);
48137         
48138     },
48139
48140     /**
48141      * Toggles the editor between standard and source edit mode.
48142      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
48143      */
48144     toggleSourceEdit : function(sourceEditMode)
48145     {
48146         this.editorcore.toggleSourceEdit(sourceEditMode);
48147         
48148         if(this.editorcore.sourceEditMode){
48149             Roo.log('editor - showing textarea');
48150             
48151 //            Roo.log('in');
48152 //            Roo.log(this.syncValue());
48153             this.editorcore.syncValue();
48154             this.el.removeClass('x-hidden');
48155             this.el.dom.removeAttribute('tabIndex');
48156             this.el.focus();
48157             this.el.dom.scrollTop = 0;
48158             
48159             
48160             for (var i = 0; i < this.toolbars.length; i++) {
48161                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48162                     this.toolbars[i].tb.hide();
48163                     this.toolbars[i].footer.hide();
48164                 }
48165             }
48166             
48167         }else{
48168             Roo.log('editor - hiding textarea');
48169 //            Roo.log('out')
48170 //            Roo.log(this.pushValue()); 
48171             this.editorcore.pushValue();
48172             
48173             this.el.addClass('x-hidden');
48174             this.el.dom.setAttribute('tabIndex', -1);
48175             
48176             for (var i = 0; i < this.toolbars.length; i++) {
48177                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48178                     this.toolbars[i].tb.show();
48179                     this.toolbars[i].footer.show();
48180                 }
48181             }
48182             
48183             //this.deferFocus();
48184         }
48185         
48186         this.setSize(this.wrap.getSize());
48187         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
48188         
48189         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
48190     },
48191  
48192     // private (for BoxComponent)
48193     adjustSize : Roo.BoxComponent.prototype.adjustSize,
48194
48195     // private (for BoxComponent)
48196     getResizeEl : function(){
48197         return this.wrap;
48198     },
48199
48200     // private (for BoxComponent)
48201     getPositionEl : function(){
48202         return this.wrap;
48203     },
48204
48205     // private
48206     initEvents : function(){
48207         this.originalValue = this.getValue();
48208     },
48209
48210     /**
48211      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48212      * @method
48213      */
48214     markInvalid : Roo.emptyFn,
48215     /**
48216      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48217      * @method
48218      */
48219     clearInvalid : Roo.emptyFn,
48220
48221     setValue : function(v){
48222         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
48223         this.editorcore.pushValue();
48224     },
48225
48226      
48227     // private
48228     deferFocus : function(){
48229         this.focus.defer(10, this);
48230     },
48231
48232     // doc'ed in Field
48233     focus : function(){
48234         this.editorcore.focus();
48235         
48236     },
48237       
48238
48239     // private
48240     onDestroy : function(){
48241         
48242         
48243         
48244         if(this.rendered){
48245             
48246             for (var i =0; i < this.toolbars.length;i++) {
48247                 // fixme - ask toolbars for heights?
48248                 this.toolbars[i].onDestroy();
48249             }
48250             
48251             this.wrap.dom.innerHTML = '';
48252             this.wrap.remove();
48253         }
48254     },
48255
48256     // private
48257     onFirstFocus : function(){
48258         //Roo.log("onFirstFocus");
48259         this.editorcore.onFirstFocus();
48260          for (var i =0; i < this.toolbars.length;i++) {
48261             this.toolbars[i].onFirstFocus();
48262         }
48263         
48264     },
48265     
48266     // private
48267     syncValue : function()
48268     {
48269         this.editorcore.syncValue();
48270     },
48271     
48272     pushValue : function()
48273     {
48274         this.editorcore.pushValue();
48275     },
48276     
48277     setStylesheets : function(stylesheets)
48278     {
48279         this.editorcore.setStylesheets(stylesheets);
48280     },
48281     
48282     removeStylesheets : function()
48283     {
48284         this.editorcore.removeStylesheets();
48285     }
48286      
48287     
48288     // hide stuff that is not compatible
48289     /**
48290      * @event blur
48291      * @hide
48292      */
48293     /**
48294      * @event change
48295      * @hide
48296      */
48297     /**
48298      * @event focus
48299      * @hide
48300      */
48301     /**
48302      * @event specialkey
48303      * @hide
48304      */
48305     /**
48306      * @cfg {String} fieldClass @hide
48307      */
48308     /**
48309      * @cfg {String} focusClass @hide
48310      */
48311     /**
48312      * @cfg {String} autoCreate @hide
48313      */
48314     /**
48315      * @cfg {String} inputType @hide
48316      */
48317     /**
48318      * @cfg {String} invalidClass @hide
48319      */
48320     /**
48321      * @cfg {String} invalidText @hide
48322      */
48323     /**
48324      * @cfg {String} msgFx @hide
48325      */
48326     /**
48327      * @cfg {String} validateOnBlur @hide
48328      */
48329 });
48330  
48331     // <script type="text/javascript">
48332 /*
48333  * Based on
48334  * Ext JS Library 1.1.1
48335  * Copyright(c) 2006-2007, Ext JS, LLC.
48336  *  
48337  
48338  */
48339
48340 /**
48341  * @class Roo.form.HtmlEditorToolbar1
48342  * Basic Toolbar
48343  * 
48344  * Usage:
48345  *
48346  new Roo.form.HtmlEditor({
48347     ....
48348     toolbars : [
48349         new Roo.form.HtmlEditorToolbar1({
48350             disable : { fonts: 1 , format: 1, ..., ... , ...],
48351             btns : [ .... ]
48352         })
48353     }
48354      
48355  * 
48356  * @cfg {Object} disable List of elements to disable..
48357  * @cfg {Array} btns List of additional buttons.
48358  * 
48359  * 
48360  * NEEDS Extra CSS? 
48361  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
48362  */
48363  
48364 Roo.form.HtmlEditor.ToolbarStandard = function(config)
48365 {
48366     
48367     Roo.apply(this, config);
48368     
48369     // default disabled, based on 'good practice'..
48370     this.disable = this.disable || {};
48371     Roo.applyIf(this.disable, {
48372         fontSize : true,
48373         colors : true,
48374         specialElements : true
48375     });
48376     
48377     
48378     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48379     // dont call parent... till later.
48380 }
48381
48382 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
48383     
48384     tb: false,
48385     
48386     rendered: false,
48387     
48388     editor : false,
48389     editorcore : false,
48390     /**
48391      * @cfg {Object} disable  List of toolbar elements to disable
48392          
48393      */
48394     disable : false,
48395     
48396     
48397      /**
48398      * @cfg {String} createLinkText The default text for the create link prompt
48399      */
48400     createLinkText : 'Please enter the URL for the link:',
48401     /**
48402      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
48403      */
48404     defaultLinkValue : 'http:/'+'/',
48405    
48406     
48407       /**
48408      * @cfg {Array} fontFamilies An array of available font families
48409      */
48410     fontFamilies : [
48411         'Arial',
48412         'Courier New',
48413         'Tahoma',
48414         'Times New Roman',
48415         'Verdana'
48416     ],
48417     
48418     specialChars : [
48419            "&#169;",
48420           "&#174;",     
48421           "&#8482;",    
48422           "&#163;" ,    
48423          // "&#8212;",    
48424           "&#8230;",    
48425           "&#247;" ,    
48426         //  "&#225;" ,     ?? a acute?
48427            "&#8364;"    , //Euro
48428        //   "&#8220;"    ,
48429         //  "&#8221;"    ,
48430         //  "&#8226;"    ,
48431           "&#176;"  //   , // degrees
48432
48433          // "&#233;"     , // e ecute
48434          // "&#250;"     , // u ecute?
48435     ],
48436     
48437     specialElements : [
48438         {
48439             text: "Insert Table",
48440             xtype: 'MenuItem',
48441             xns : Roo.Menu,
48442             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
48443                 
48444         },
48445         {    
48446             text: "Insert Image",
48447             xtype: 'MenuItem',
48448             xns : Roo.Menu,
48449             ihtml : '<img src="about:blank"/>'
48450             
48451         }
48452         
48453          
48454     ],
48455     
48456     
48457     inputElements : [ 
48458             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
48459             "input:submit", "input:button", "select", "textarea", "label" ],
48460     formats : [
48461         ["p"] ,  
48462         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
48463         ["pre"],[ "code"], 
48464         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
48465         ['div'],['span'],
48466         ['sup'],['sub']
48467     ],
48468     
48469     cleanStyles : [
48470         "font-size"
48471     ],
48472      /**
48473      * @cfg {String} defaultFont default font to use.
48474      */
48475     defaultFont: 'tahoma',
48476    
48477     fontSelect : false,
48478     
48479     
48480     formatCombo : false,
48481     
48482     init : function(editor)
48483     {
48484         this.editor = editor;
48485         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48486         var editorcore = this.editorcore;
48487         
48488         var _t = this;
48489         
48490         var fid = editorcore.frameId;
48491         var etb = this;
48492         function btn(id, toggle, handler){
48493             var xid = fid + '-'+ id ;
48494             return {
48495                 id : xid,
48496                 cmd : id,
48497                 cls : 'x-btn-icon x-edit-'+id,
48498                 enableToggle:toggle !== false,
48499                 scope: _t, // was editor...
48500                 handler:handler||_t.relayBtnCmd,
48501                 clickEvent:'mousedown',
48502                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48503                 tabIndex:-1
48504             };
48505         }
48506         
48507         
48508         
48509         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48510         this.tb = tb;
48511          // stop form submits
48512         tb.el.on('click', function(e){
48513             e.preventDefault(); // what does this do?
48514         });
48515
48516         if(!this.disable.font) { // && !Roo.isSafari){
48517             /* why no safari for fonts 
48518             editor.fontSelect = tb.el.createChild({
48519                 tag:'select',
48520                 tabIndex: -1,
48521                 cls:'x-font-select',
48522                 html: this.createFontOptions()
48523             });
48524             
48525             editor.fontSelect.on('change', function(){
48526                 var font = editor.fontSelect.dom.value;
48527                 editor.relayCmd('fontname', font);
48528                 editor.deferFocus();
48529             }, editor);
48530             
48531             tb.add(
48532                 editor.fontSelect.dom,
48533                 '-'
48534             );
48535             */
48536             
48537         };
48538         if(!this.disable.formats){
48539             this.formatCombo = new Roo.form.ComboBox({
48540                 store: new Roo.data.SimpleStore({
48541                     id : 'tag',
48542                     fields: ['tag'],
48543                     data : this.formats // from states.js
48544                 }),
48545                 blockFocus : true,
48546                 name : '',
48547                 //autoCreate : {tag: "div",  size: "20"},
48548                 displayField:'tag',
48549                 typeAhead: false,
48550                 mode: 'local',
48551                 editable : false,
48552                 triggerAction: 'all',
48553                 emptyText:'Add tag',
48554                 selectOnFocus:true,
48555                 width:135,
48556                 listeners : {
48557                     'select': function(c, r, i) {
48558                         editorcore.insertTag(r.get('tag'));
48559                         editor.focus();
48560                     }
48561                 }
48562
48563             });
48564             tb.addField(this.formatCombo);
48565             
48566         }
48567         
48568         if(!this.disable.format){
48569             tb.add(
48570                 btn('bold'),
48571                 btn('italic'),
48572                 btn('underline'),
48573                 btn('strikethrough')
48574             );
48575         };
48576         if(!this.disable.fontSize){
48577             tb.add(
48578                 '-',
48579                 
48580                 
48581                 btn('increasefontsize', false, editorcore.adjustFont),
48582                 btn('decreasefontsize', false, editorcore.adjustFont)
48583             );
48584         };
48585         
48586         
48587         if(!this.disable.colors){
48588             tb.add(
48589                 '-', {
48590                     id:editorcore.frameId +'-forecolor',
48591                     cls:'x-btn-icon x-edit-forecolor',
48592                     clickEvent:'mousedown',
48593                     tooltip: this.buttonTips['forecolor'] || undefined,
48594                     tabIndex:-1,
48595                     menu : new Roo.menu.ColorMenu({
48596                         allowReselect: true,
48597                         focus: Roo.emptyFn,
48598                         value:'000000',
48599                         plain:true,
48600                         selectHandler: function(cp, color){
48601                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
48602                             editor.deferFocus();
48603                         },
48604                         scope: editorcore,
48605                         clickEvent:'mousedown'
48606                     })
48607                 }, {
48608                     id:editorcore.frameId +'backcolor',
48609                     cls:'x-btn-icon x-edit-backcolor',
48610                     clickEvent:'mousedown',
48611                     tooltip: this.buttonTips['backcolor'] || undefined,
48612                     tabIndex:-1,
48613                     menu : new Roo.menu.ColorMenu({
48614                         focus: Roo.emptyFn,
48615                         value:'FFFFFF',
48616                         plain:true,
48617                         allowReselect: true,
48618                         selectHandler: function(cp, color){
48619                             if(Roo.isGecko){
48620                                 editorcore.execCmd('useCSS', false);
48621                                 editorcore.execCmd('hilitecolor', color);
48622                                 editorcore.execCmd('useCSS', true);
48623                                 editor.deferFocus();
48624                             }else{
48625                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
48626                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
48627                                 editor.deferFocus();
48628                             }
48629                         },
48630                         scope:editorcore,
48631                         clickEvent:'mousedown'
48632                     })
48633                 }
48634             );
48635         };
48636         // now add all the items...
48637         
48638
48639         if(!this.disable.alignments){
48640             tb.add(
48641                 '-',
48642                 btn('justifyleft'),
48643                 btn('justifycenter'),
48644                 btn('justifyright')
48645             );
48646         };
48647
48648         //if(!Roo.isSafari){
48649             if(!this.disable.links){
48650                 tb.add(
48651                     '-',
48652                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
48653                 );
48654             };
48655
48656             if(!this.disable.lists){
48657                 tb.add(
48658                     '-',
48659                     btn('insertorderedlist'),
48660                     btn('insertunorderedlist')
48661                 );
48662             }
48663             if(!this.disable.sourceEdit){
48664                 tb.add(
48665                     '-',
48666                     btn('sourceedit', true, function(btn){
48667                         this.toggleSourceEdit(btn.pressed);
48668                     })
48669                 );
48670             }
48671         //}
48672         
48673         var smenu = { };
48674         // special menu.. - needs to be tidied up..
48675         if (!this.disable.special) {
48676             smenu = {
48677                 text: "&#169;",
48678                 cls: 'x-edit-none',
48679                 
48680                 menu : {
48681                     items : []
48682                 }
48683             };
48684             for (var i =0; i < this.specialChars.length; i++) {
48685                 smenu.menu.items.push({
48686                     
48687                     html: this.specialChars[i],
48688                     handler: function(a,b) {
48689                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
48690                         //editor.insertAtCursor(a.html);
48691                         
48692                     },
48693                     tabIndex:-1
48694                 });
48695             }
48696             
48697             
48698             tb.add(smenu);
48699             
48700             
48701         }
48702         
48703         var cmenu = { };
48704         if (!this.disable.cleanStyles) {
48705             cmenu = {
48706                 cls: 'x-btn-icon x-btn-clear',
48707                 
48708                 menu : {
48709                     items : []
48710                 }
48711             };
48712             for (var i =0; i < this.cleanStyles.length; i++) {
48713                 cmenu.menu.items.push({
48714                     actiontype : this.cleanStyles[i],
48715                     html: 'Remove ' + this.cleanStyles[i],
48716                     handler: function(a,b) {
48717 //                        Roo.log(a);
48718 //                        Roo.log(b);
48719                         var c = Roo.get(editorcore.doc.body);
48720                         c.select('[style]').each(function(s) {
48721                             s.dom.style.removeProperty(a.actiontype);
48722                         });
48723                         editorcore.syncValue();
48724                     },
48725                     tabIndex:-1
48726                 });
48727             }
48728             cmenu.menu.items.push({
48729                 actiontype : 'tablewidths',
48730                 html: 'Remove Table Widths',
48731                 handler: function(a,b) {
48732                     editorcore.cleanTableWidths();
48733                     editorcore.syncValue();
48734                 },
48735                 tabIndex:-1
48736             });
48737             cmenu.menu.items.push({
48738                 actiontype : 'word',
48739                 html: 'Remove MS Word Formating',
48740                 handler: function(a,b) {
48741                     editorcore.cleanWord();
48742                     editorcore.syncValue();
48743                 },
48744                 tabIndex:-1
48745             });
48746             
48747             cmenu.menu.items.push({
48748                 actiontype : 'all',
48749                 html: 'Remove All Styles',
48750                 handler: function(a,b) {
48751                     
48752                     var c = Roo.get(editorcore.doc.body);
48753                     c.select('[style]').each(function(s) {
48754                         s.dom.removeAttribute('style');
48755                     });
48756                     editorcore.syncValue();
48757                 },
48758                 tabIndex:-1
48759             });
48760             
48761             cmenu.menu.items.push({
48762                 actiontype : 'all',
48763                 html: 'Remove All CSS Classes',
48764                 handler: function(a,b) {
48765                     
48766                     var c = Roo.get(editorcore.doc.body);
48767                     c.select('[class]').each(function(s) {
48768                         s.dom.removeAttribute('class');
48769                     });
48770                     editorcore.cleanWord();
48771                     editorcore.syncValue();
48772                 },
48773                 tabIndex:-1
48774             });
48775             
48776              cmenu.menu.items.push({
48777                 actiontype : 'tidy',
48778                 html: 'Tidy HTML Source',
48779                 handler: function(a,b) {
48780                     new Roo.htmleditor.Tidy(editorcore.doc.body);
48781                     editorcore.syncValue();
48782                 },
48783                 tabIndex:-1
48784             });
48785             
48786             
48787             tb.add(cmenu);
48788         }
48789          
48790         if (!this.disable.specialElements) {
48791             var semenu = {
48792                 text: "Other;",
48793                 cls: 'x-edit-none',
48794                 menu : {
48795                     items : []
48796                 }
48797             };
48798             for (var i =0; i < this.specialElements.length; i++) {
48799                 semenu.menu.items.push(
48800                     Roo.apply({ 
48801                         handler: function(a,b) {
48802                             editor.insertAtCursor(this.ihtml);
48803                         }
48804                     }, this.specialElements[i])
48805                 );
48806                     
48807             }
48808             
48809             tb.add(semenu);
48810             
48811             
48812         }
48813          
48814         
48815         if (this.btns) {
48816             for(var i =0; i< this.btns.length;i++) {
48817                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
48818                 b.cls =  'x-edit-none';
48819                 
48820                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
48821                     b.cls += ' x-init-enable';
48822                 }
48823                 
48824                 b.scope = editorcore;
48825                 tb.add(b);
48826             }
48827         
48828         }
48829         
48830         
48831         
48832         // disable everything...
48833         
48834         this.tb.items.each(function(item){
48835             
48836            if(
48837                 item.id != editorcore.frameId+ '-sourceedit' && 
48838                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
48839             ){
48840                 
48841                 item.disable();
48842             }
48843         });
48844         this.rendered = true;
48845         
48846         // the all the btns;
48847         editor.on('editorevent', this.updateToolbar, this);
48848         // other toolbars need to implement this..
48849         //editor.on('editmodechange', this.updateToolbar, this);
48850     },
48851     
48852     
48853     relayBtnCmd : function(btn) {
48854         this.editorcore.relayCmd(btn.cmd);
48855     },
48856     // private used internally
48857     createLink : function(){
48858         Roo.log("create link?");
48859         var url = prompt(this.createLinkText, this.defaultLinkValue);
48860         if(url && url != 'http:/'+'/'){
48861             this.editorcore.relayCmd('createlink', url);
48862         }
48863     },
48864
48865     
48866     /**
48867      * Protected method that will not generally be called directly. It triggers
48868      * a toolbar update by reading the markup state of the current selection in the editor.
48869      */
48870     updateToolbar: function(){
48871
48872         if(!this.editorcore.activated){
48873             this.editor.onFirstFocus();
48874             return;
48875         }
48876
48877         var btns = this.tb.items.map, 
48878             doc = this.editorcore.doc,
48879             frameId = this.editorcore.frameId;
48880
48881         if(!this.disable.font && !Roo.isSafari){
48882             /*
48883             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
48884             if(name != this.fontSelect.dom.value){
48885                 this.fontSelect.dom.value = name;
48886             }
48887             */
48888         }
48889         if(!this.disable.format){
48890             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
48891             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
48892             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
48893             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
48894         }
48895         if(!this.disable.alignments){
48896             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
48897             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
48898             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
48899         }
48900         if(!Roo.isSafari && !this.disable.lists){
48901             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
48902             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
48903         }
48904         
48905         var ans = this.editorcore.getAllAncestors();
48906         if (this.formatCombo) {
48907             
48908             
48909             var store = this.formatCombo.store;
48910             this.formatCombo.setValue("");
48911             for (var i =0; i < ans.length;i++) {
48912                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
48913                     // select it..
48914                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
48915                     break;
48916                 }
48917             }
48918         }
48919         
48920         
48921         
48922         // hides menus... - so this cant be on a menu...
48923         Roo.menu.MenuMgr.hideAll();
48924
48925         //this.editorsyncValue();
48926     },
48927    
48928     
48929     createFontOptions : function(){
48930         var buf = [], fs = this.fontFamilies, ff, lc;
48931         
48932         
48933         
48934         for(var i = 0, len = fs.length; i< len; i++){
48935             ff = fs[i];
48936             lc = ff.toLowerCase();
48937             buf.push(
48938                 '<option value="',lc,'" style="font-family:',ff,';"',
48939                     (this.defaultFont == lc ? ' selected="true">' : '>'),
48940                     ff,
48941                 '</option>'
48942             );
48943         }
48944         return buf.join('');
48945     },
48946     
48947     toggleSourceEdit : function(sourceEditMode){
48948         
48949         Roo.log("toolbar toogle");
48950         if(sourceEditMode === undefined){
48951             sourceEditMode = !this.sourceEditMode;
48952         }
48953         this.sourceEditMode = sourceEditMode === true;
48954         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
48955         // just toggle the button?
48956         if(btn.pressed !== this.sourceEditMode){
48957             btn.toggle(this.sourceEditMode);
48958             return;
48959         }
48960         
48961         if(sourceEditMode){
48962             Roo.log("disabling buttons");
48963             this.tb.items.each(function(item){
48964                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
48965                     item.disable();
48966                 }
48967             });
48968           
48969         }else{
48970             Roo.log("enabling buttons");
48971             if(this.editorcore.initialized){
48972                 this.tb.items.each(function(item){
48973                     item.enable();
48974                 });
48975             }
48976             
48977         }
48978         Roo.log("calling toggole on editor");
48979         // tell the editor that it's been pressed..
48980         this.editor.toggleSourceEdit(sourceEditMode);
48981        
48982     },
48983      /**
48984      * Object collection of toolbar tooltips for the buttons in the editor. The key
48985      * is the command id associated with that button and the value is a valid QuickTips object.
48986      * For example:
48987 <pre><code>
48988 {
48989     bold : {
48990         title: 'Bold (Ctrl+B)',
48991         text: 'Make the selected text bold.',
48992         cls: 'x-html-editor-tip'
48993     },
48994     italic : {
48995         title: 'Italic (Ctrl+I)',
48996         text: 'Make the selected text italic.',
48997         cls: 'x-html-editor-tip'
48998     },
48999     ...
49000 </code></pre>
49001     * @type Object
49002      */
49003     buttonTips : {
49004         bold : {
49005             title: 'Bold (Ctrl+B)',
49006             text: 'Make the selected text bold.',
49007             cls: 'x-html-editor-tip'
49008         },
49009         italic : {
49010             title: 'Italic (Ctrl+I)',
49011             text: 'Make the selected text italic.',
49012             cls: 'x-html-editor-tip'
49013         },
49014         underline : {
49015             title: 'Underline (Ctrl+U)',
49016             text: 'Underline the selected text.',
49017             cls: 'x-html-editor-tip'
49018         },
49019         strikethrough : {
49020             title: 'Strikethrough',
49021             text: 'Strikethrough the selected text.',
49022             cls: 'x-html-editor-tip'
49023         },
49024         increasefontsize : {
49025             title: 'Grow Text',
49026             text: 'Increase the font size.',
49027             cls: 'x-html-editor-tip'
49028         },
49029         decreasefontsize : {
49030             title: 'Shrink Text',
49031             text: 'Decrease the font size.',
49032             cls: 'x-html-editor-tip'
49033         },
49034         backcolor : {
49035             title: 'Text Highlight Color',
49036             text: 'Change the background color of the selected text.',
49037             cls: 'x-html-editor-tip'
49038         },
49039         forecolor : {
49040             title: 'Font Color',
49041             text: 'Change the color of the selected text.',
49042             cls: 'x-html-editor-tip'
49043         },
49044         justifyleft : {
49045             title: 'Align Text Left',
49046             text: 'Align text to the left.',
49047             cls: 'x-html-editor-tip'
49048         },
49049         justifycenter : {
49050             title: 'Center Text',
49051             text: 'Center text in the editor.',
49052             cls: 'x-html-editor-tip'
49053         },
49054         justifyright : {
49055             title: 'Align Text Right',
49056             text: 'Align text to the right.',
49057             cls: 'x-html-editor-tip'
49058         },
49059         insertunorderedlist : {
49060             title: 'Bullet List',
49061             text: 'Start a bulleted list.',
49062             cls: 'x-html-editor-tip'
49063         },
49064         insertorderedlist : {
49065             title: 'Numbered List',
49066             text: 'Start a numbered list.',
49067             cls: 'x-html-editor-tip'
49068         },
49069         createlink : {
49070             title: 'Hyperlink',
49071             text: 'Make the selected text a hyperlink.',
49072             cls: 'x-html-editor-tip'
49073         },
49074         sourceedit : {
49075             title: 'Source Edit',
49076             text: 'Switch to source editing mode.',
49077             cls: 'x-html-editor-tip'
49078         }
49079     },
49080     // private
49081     onDestroy : function(){
49082         if(this.rendered){
49083             
49084             this.tb.items.each(function(item){
49085                 if(item.menu){
49086                     item.menu.removeAll();
49087                     if(item.menu.el){
49088                         item.menu.el.destroy();
49089                     }
49090                 }
49091                 item.destroy();
49092             });
49093              
49094         }
49095     },
49096     onFirstFocus: function() {
49097         this.tb.items.each(function(item){
49098            item.enable();
49099         });
49100     }
49101 });
49102
49103
49104
49105
49106 // <script type="text/javascript">
49107 /*
49108  * Based on
49109  * Ext JS Library 1.1.1
49110  * Copyright(c) 2006-2007, Ext JS, LLC.
49111  *  
49112  
49113  */
49114
49115  
49116 /**
49117  * @class Roo.form.HtmlEditor.ToolbarContext
49118  * Context Toolbar
49119  * 
49120  * Usage:
49121  *
49122  new Roo.form.HtmlEditor({
49123     ....
49124     toolbars : [
49125         { xtype: 'ToolbarStandard', styles : {} }
49126         { xtype: 'ToolbarContext', disable : {} }
49127     ]
49128 })
49129
49130      
49131  * 
49132  * @config : {Object} disable List of elements to disable.. (not done yet.)
49133  * @config : {Object} styles  Map of styles available.
49134  * 
49135  */
49136
49137 Roo.form.HtmlEditor.ToolbarContext = function(config)
49138 {
49139     
49140     Roo.apply(this, config);
49141     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49142     // dont call parent... till later.
49143     this.styles = this.styles || {};
49144 }
49145
49146  
49147
49148 Roo.form.HtmlEditor.ToolbarContext.types = {
49149     'IMG' : [
49150         {
49151             name : 'width',
49152             title: "Width",
49153             width: 40
49154         },
49155         {
49156             name : 'height',
49157             title: "Height",
49158             width: 40
49159         },
49160         {
49161             name : 'align',
49162             title: "Align",
49163             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49164             width : 80
49165             
49166         },
49167         {
49168             name : 'border',
49169             title: "Border",
49170             width: 40
49171         },
49172         {
49173             name : 'alt',
49174             title: "Alt",
49175             width: 120
49176         },
49177         {
49178             name : 'src',
49179             title: "Src",
49180             width: 220
49181         }
49182         
49183     ],
49184     
49185     'FIGURE' : [
49186         {
49187             name : 'align',
49188             title: "Align",
49189             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49190             width : 80  
49191         }
49192     ],
49193     'A' : [
49194         {
49195             name : 'name',
49196             title: "Name",
49197             width: 50
49198         },
49199         {
49200             name : 'target',
49201             title: "Target",
49202             width: 120
49203         },
49204         {
49205             name : 'href',
49206             title: "Href",
49207             width: 220
49208         } // border?
49209         
49210     ],
49211     
49212     'INPUT' : [
49213         {
49214             name : 'name',
49215             title: "name",
49216             width: 120
49217         },
49218         {
49219             name : 'value',
49220             title: "Value",
49221             width: 120
49222         },
49223         {
49224             name : 'width',
49225             title: "Width",
49226             width: 40
49227         }
49228     ],
49229     'LABEL' : [
49230          {
49231             name : 'for',
49232             title: "For",
49233             width: 120
49234         }
49235     ],
49236     'TEXTAREA' : [
49237         {
49238             name : 'name',
49239             title: "name",
49240             width: 120
49241         },
49242         {
49243             name : 'rows',
49244             title: "Rows",
49245             width: 20
49246         },
49247         {
49248             name : 'cols',
49249             title: "Cols",
49250             width: 20
49251         }
49252     ],
49253     'SELECT' : [
49254         {
49255             name : 'name',
49256             title: "name",
49257             width: 120
49258         },
49259         {
49260             name : 'selectoptions',
49261             title: "Options",
49262             width: 200
49263         }
49264     ],
49265     
49266     // should we really allow this??
49267     // should this just be 
49268     'BODY' : [
49269         
49270         {
49271             name : 'title',
49272             title: "Title",
49273             width: 200,
49274             disabled : true
49275         }
49276     ],
49277  
49278     '*' : [
49279         // empty.
49280     ]
49281
49282 };
49283
49284 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
49285 Roo.form.HtmlEditor.ToolbarContext.stores = false;
49286
49287 Roo.form.HtmlEditor.ToolbarContext.options = {
49288         'font-family'  : [ 
49289                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
49290                 [ 'Courier New', 'Courier New'],
49291                 [ 'Tahoma', 'Tahoma'],
49292                 [ 'Times New Roman,serif', 'Times'],
49293                 [ 'Verdana','Verdana' ]
49294         ]
49295 };
49296
49297 // fixme - these need to be configurable..
49298  
49299
49300 //Roo.form.HtmlEditor.ToolbarContext.types
49301
49302
49303 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
49304     
49305     tb: false,
49306     
49307     rendered: false,
49308     
49309     editor : false,
49310     editorcore : false,
49311     /**
49312      * @cfg {Object} disable  List of toolbar elements to disable
49313          
49314      */
49315     disable : false,
49316     /**
49317      * @cfg {Object} styles List of styles 
49318      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
49319      *
49320      * These must be defined in the page, so they get rendered correctly..
49321      * .headline { }
49322      * TD.underline { }
49323      * 
49324      */
49325     styles : false,
49326     
49327     options: false,
49328     
49329     toolbars : false,
49330     
49331     init : function(editor)
49332     {
49333         this.editor = editor;
49334         this.editorcore = editor.editorcore ? editor.editorcore : editor;
49335         var editorcore = this.editorcore;
49336         
49337         var fid = editorcore.frameId;
49338         var etb = this;
49339         function btn(id, toggle, handler){
49340             var xid = fid + '-'+ id ;
49341             return {
49342                 id : xid,
49343                 cmd : id,
49344                 cls : 'x-btn-icon x-edit-'+id,
49345                 enableToggle:toggle !== false,
49346                 scope: editorcore, // was editor...
49347                 handler:handler||editorcore.relayBtnCmd,
49348                 clickEvent:'mousedown',
49349                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49350                 tabIndex:-1
49351             };
49352         }
49353         // create a new element.
49354         var wdiv = editor.wrap.createChild({
49355                 tag: 'div'
49356             }, editor.wrap.dom.firstChild.nextSibling, true);
49357         
49358         // can we do this more than once??
49359         
49360          // stop form submits
49361       
49362  
49363         // disable everything...
49364         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
49365         this.toolbars = {};
49366            
49367         for (var i in  ty) {
49368           
49369             this.toolbars[i] = this.buildToolbar(ty[i],i);
49370         }
49371         this.tb = this.toolbars.BODY;
49372         this.tb.el.show();
49373         this.buildFooter();
49374         this.footer.show();
49375         editor.on('hide', function( ) { this.footer.hide() }, this);
49376         editor.on('show', function( ) { this.footer.show() }, this);
49377         
49378          
49379         this.rendered = true;
49380         
49381         // the all the btns;
49382         editor.on('editorevent', this.updateToolbar, this);
49383         // other toolbars need to implement this..
49384         //editor.on('editmodechange', this.updateToolbar, this);
49385     },
49386     
49387     
49388     
49389     /**
49390      * Protected method that will not generally be called directly. It triggers
49391      * a toolbar update by reading the markup state of the current selection in the editor.
49392      *
49393      * Note you can force an update by calling on('editorevent', scope, false)
49394      */
49395     updateToolbar: function(editor ,ev, sel)
49396     {
49397         
49398         if (ev) {
49399             ev.stopEvent(); // se if we can stop this looping with mutiple events.
49400         }
49401         
49402         //Roo.log(ev);
49403         // capture mouse up - this is handy for selecting images..
49404         // perhaps should go somewhere else...
49405         if(!this.editorcore.activated){
49406              this.editor.onFirstFocus();
49407             return;
49408         }
49409         Roo.log(ev ? ev.target : 'NOTARGET');
49410         
49411         
49412         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
49413         // selectNode - might want to handle IE?
49414         
49415         
49416         
49417         if (ev &&
49418             (ev.type == 'mouseup' || ev.type == 'click' ) &&
49419             ev.target && ev.target != 'BODY' ) { // && ev.target.tagName == 'IMG') {
49420             // they have click on an image...
49421             // let's see if we can change the selection...
49422             sel = ev.target;
49423             
49424             // this triggers looping?
49425             //this.editorcore.selectNode(sel);
49426              
49427         }  
49428         
49429       
49430         //var updateFooter = sel ? false : true; 
49431         
49432         
49433         var ans = this.editorcore.getAllAncestors();
49434         
49435         // pick
49436         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
49437         
49438         if (!sel) { 
49439             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
49440             sel = sel ? sel : this.editorcore.doc.body;
49441             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
49442             
49443         }
49444         
49445         var tn = sel.tagName.toUpperCase();
49446         var lastSel = this.tb.selectedNode;
49447         this.tb.selectedNode = sel;
49448         var left_label = tn;
49449         
49450         // ok see if we are editing a block?
49451         var sel_el = Roo.get(sel);
49452         var db = false;
49453         // you are not actually selecting the block.
49454         if (sel && sel.hasAttribute('data-block')) {
49455             db = sel;
49456         } else if (sel && !sel.hasAttribute('contenteditable')) {
49457             db = sel_el.findParent('[data-block]');
49458             var cepar = sel_el.findParent('[contenteditable=true]');
49459             if (db && cepar && cepar.tagName != 'BODY') {
49460                db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
49461             }   
49462         }
49463         
49464         
49465         var block = false;
49466         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
49467         if (db) {
49468             block = Roo.htmleditor.Block.factory(db);
49469             if (block) {
49470                 tn = 'BLOCK.' + db.getAttribute('data-block');
49471                 
49472                 //this.editorcore.selectNode(db);
49473                 if (typeof(this.toolbars[tn]) == 'undefined') {
49474                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
49475                 }
49476                 this.toolbars[tn].selectedNode = db;
49477                 left_label = block.friendly_name;
49478                 ans = this.editorcore.getAllAncestors();
49479             }
49480             
49481                 
49482             
49483         }
49484         
49485         
49486         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
49487             return; // no change?
49488         }
49489         
49490         
49491           
49492         this.tb.el.hide();
49493         ///console.log("show: " + tn);
49494         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
49495         
49496         this.tb.el.show();
49497         // update name
49498         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
49499         
49500         
49501         // update attributes
49502         if (block) {
49503              
49504             this.tb.fields.each(function(e) {
49505                 e.setValue(block[e.name]);
49506             });
49507             
49508             
49509         } else  if (this.tb.fields && this.tb.selectedNode) {
49510             this.tb.fields.each( function(e) {
49511                 if (e.stylename) {
49512                     e.setValue(this.tb.selectedNode.style[e.stylename]);
49513                     return;
49514                 } 
49515                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
49516             }, this);
49517             this.updateToolbarStyles(this.tb.selectedNode);  
49518         }
49519         
49520         
49521        
49522         Roo.menu.MenuMgr.hideAll();
49523
49524         
49525         
49526     
49527         // update the footer
49528         //
49529         this.updateFooter(ans);
49530              
49531     },
49532     
49533     updateToolbarStyles : function(sel)
49534     {
49535         var hasStyles = false;
49536         for(var i in this.styles) {
49537             hasStyles = true;
49538             break;
49539         }
49540         
49541         // update styles
49542         if (hasStyles && this.tb.hasStyles) { 
49543             var st = this.tb.fields.item(0);
49544             
49545             st.store.removeAll();
49546             var cn = sel.className.split(/\s+/);
49547             
49548             var avs = [];
49549             if (this.styles['*']) {
49550                 
49551                 Roo.each(this.styles['*'], function(v) {
49552                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49553                 });
49554             }
49555             if (this.styles[tn]) { 
49556                 Roo.each(this.styles[tn], function(v) {
49557                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49558                 });
49559             }
49560             
49561             st.store.loadData(avs);
49562             st.collapse();
49563             st.setValue(cn);
49564         }
49565     },
49566     
49567      
49568     updateFooter : function(ans)
49569     {
49570         var html = '';
49571         if (ans === false) {
49572             this.footDisp.dom.innerHTML = '';
49573             return;
49574         }
49575         
49576         this.footerEls = ans.reverse();
49577         Roo.each(this.footerEls, function(a,i) {
49578             if (!a) { return; }
49579             html += html.length ? ' &gt; '  :  '';
49580             
49581             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
49582             
49583         });
49584        
49585         // 
49586         var sz = this.footDisp.up('td').getSize();
49587         this.footDisp.dom.style.width = (sz.width -10) + 'px';
49588         this.footDisp.dom.style.marginLeft = '5px';
49589         
49590         this.footDisp.dom.style.overflow = 'hidden';
49591         
49592         this.footDisp.dom.innerHTML = html;
49593             
49594         
49595     },
49596    
49597        
49598     // private
49599     onDestroy : function(){
49600         if(this.rendered){
49601             
49602             this.tb.items.each(function(item){
49603                 if(item.menu){
49604                     item.menu.removeAll();
49605                     if(item.menu.el){
49606                         item.menu.el.destroy();
49607                     }
49608                 }
49609                 item.destroy();
49610             });
49611              
49612         }
49613     },
49614     onFirstFocus: function() {
49615         // need to do this for all the toolbars..
49616         this.tb.items.each(function(item){
49617            item.enable();
49618         });
49619     },
49620     buildToolbar: function(tlist, nm, friendly_name, block)
49621     {
49622         var editor = this.editor;
49623         var editorcore = this.editorcore;
49624          // create a new element.
49625         var wdiv = editor.wrap.createChild({
49626                 tag: 'div'
49627             }, editor.wrap.dom.firstChild.nextSibling, true);
49628         
49629        
49630         var tb = new Roo.Toolbar(wdiv);
49631         this.tb = tb;
49632         if (tlist === false && block) {
49633             tlist = block.contextMenu(this);
49634         }
49635         
49636         tb.hasStyles = false;
49637         tb.name = nm;
49638         
49639         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
49640         
49641         var styles = Array.from(this.styles);
49642         
49643         
49644         // styles...
49645         if (styles && styles.length) {
49646             tb.hasStyles = true;
49647             // this needs a multi-select checkbox...
49648             tb.addField( new Roo.form.ComboBox({
49649                 store: new Roo.data.SimpleStore({
49650                     id : 'val',
49651                     fields: ['val', 'selected'],
49652                     data : [] 
49653                 }),
49654                 name : '-roo-edit-className',
49655                 attrname : 'className',
49656                 displayField: 'val',
49657                 typeAhead: false,
49658                 mode: 'local',
49659                 editable : false,
49660                 triggerAction: 'all',
49661                 emptyText:'Select Style',
49662                 selectOnFocus:true,
49663                 width: 130,
49664                 listeners : {
49665                     'select': function(c, r, i) {
49666                         // initial support only for on class per el..
49667                         tb.selectedNode.className =  r ? r.get('val') : '';
49668                         editorcore.syncValue();
49669                     }
49670                 }
49671     
49672             }));
49673         }
49674         
49675         var tbc = Roo.form.HtmlEditor.ToolbarContext;
49676         
49677         
49678         for (var i = 0; i < tlist.length; i++) {
49679             
49680             // newer versions will use xtype cfg to create menus.
49681             if (typeof(tlist[i].xtype) != 'undefined') {
49682                 
49683                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
49684                 
49685                 
49686                 continue;
49687             }
49688             
49689             var item = tlist[i];
49690             tb.add(item.title + ":&nbsp;");
49691             
49692             
49693             //optname == used so you can configure the options available..
49694             var opts = item.opts ? item.opts : false;
49695             if (item.optname) { // use the b
49696                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
49697            
49698             }
49699             
49700             if (opts) {
49701                 // opts == pulldown..
49702                 tb.addField( new Roo.form.ComboBox({
49703                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
49704                         id : 'val',
49705                         fields: ['val', 'display'],
49706                         data : opts  
49707                     }),
49708                     name : '-roo-edit-' + tlist[i].name,
49709                     
49710                     attrname : tlist[i].name,
49711                     stylename : item.style ? item.style : false,
49712                     
49713                     displayField: item.displayField ? item.displayField : 'val',
49714                     valueField :  'val',
49715                     typeAhead: false,
49716                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
49717                     editable : false,
49718                     triggerAction: 'all',
49719                     emptyText:'Select',
49720                     selectOnFocus:true,
49721                     width: item.width ? item.width  : 130,
49722                     listeners : {
49723                         'select': function(c, r, i) {
49724                             if (tb.selectedNode.hasAttribute('data-block')) {
49725                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
49726                                 b[c.attrname] = r.get('val');
49727                                 b.updateElement(tb.selectedNode);
49728                                 editorcore.syncValue();
49729                                 return;
49730                             }
49731                             
49732                             if (c.stylename) {
49733                                 tb.selectedNode.style[c.stylename] =  r.get('val');
49734                                 editorcore.syncValue();
49735                                 return;
49736                             }
49737                             if (r === false) {
49738                                 tb.selectedNode.removeAttribute(c.attrname);
49739                                 editorcore.syncValue();
49740                                 return;
49741                             }
49742                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
49743                             editorcore.syncValue();
49744                         }
49745                     }
49746
49747                 }));
49748                 continue;
49749                     
49750                  
49751                 /*
49752                 tb.addField( new Roo.form.TextField({
49753                     name: i,
49754                     width: 100,
49755                     //allowBlank:false,
49756                     value: ''
49757                 }));
49758                 continue;
49759                 */
49760             }
49761             tb.addField( new Roo.form.TextField({
49762                 name: '-roo-edit-' + tlist[i].name,
49763                 attrname : tlist[i].name,
49764                 
49765                 width: item.width,
49766                 //allowBlank:true,
49767                 value: '',
49768                 listeners: {
49769                     'change' : function(f, nv, ov) {
49770                         
49771                         if (tb.selectedNode.hasAttribute('data-block')) {
49772                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
49773                             b[f.attrname] = nv;
49774                             b.updateElement(tb.selectedNode);
49775                             editorcore.syncValue();
49776                             return;
49777                         }
49778                         
49779                         tb.selectedNode.setAttribute(f.attrname, nv);
49780                         editorcore.syncValue();
49781                     }
49782                 }
49783             }));
49784              
49785         }
49786         
49787         var _this = this;
49788         
49789         if(nm == 'BODY'){
49790             tb.addSeparator();
49791         
49792             tb.addButton( {
49793                 text: 'Stylesheets',
49794
49795                 listeners : {
49796                     click : function ()
49797                     {
49798                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
49799                     }
49800                 }
49801             });
49802         }
49803         
49804         tb.addFill();
49805         tb.addButton({
49806             text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
49807     
49808             listeners : {
49809                 click : function ()
49810                 {
49811                     var sn = tb.selectedNode;
49812                     if (block) {
49813                         sn = Roo.htmleditor.Block.factory(tb.selectedNode).removalNode();
49814                         
49815                     }
49816                     if (!sn) {
49817                         return;
49818                     }
49819                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
49820                     if (sn.hasAttribute('data-block')) {
49821                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
49822                         sn.parentNode.removeChild(sn);
49823                         
49824                     } else if (sn && sn.tagName != 'BODY') {
49825                         // remove and keep parents.
49826                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
49827                         a.removeTag(sn);
49828                     }
49829                     
49830                     
49831                     var range = editorcore.createRange();
49832         
49833                     range.setStart(stn,0);
49834                     range.setEnd(stn,0); 
49835                     var selection = editorcore.getSelection();
49836                     selection.removeAllRanges();
49837                     selection.addRange(range);
49838                     
49839                     
49840                     //_this.updateToolbar(null, null, pn);
49841                     _this.updateToolbar(null, null, null);
49842                     _this.updateFooter(false);
49843                     
49844                 }
49845             }
49846             
49847                     
49848                 
49849             
49850         });
49851         
49852         
49853         tb.el.on('click', function(e){
49854             e.preventDefault(); // what does this do?
49855         });
49856         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
49857         tb.el.hide();
49858         
49859         // dont need to disable them... as they will get hidden
49860         return tb;
49861          
49862         
49863     },
49864     buildFooter : function()
49865     {
49866         
49867         var fel = this.editor.wrap.createChild();
49868         this.footer = new Roo.Toolbar(fel);
49869         // toolbar has scrolly on left / right?
49870         var footDisp= new Roo.Toolbar.Fill();
49871         var _t = this;
49872         this.footer.add(
49873             {
49874                 text : '&lt;',
49875                 xtype: 'Button',
49876                 handler : function() {
49877                     _t.footDisp.scrollTo('left',0,true)
49878                 }
49879             }
49880         );
49881         this.footer.add( footDisp );
49882         this.footer.add( 
49883             {
49884                 text : '&gt;',
49885                 xtype: 'Button',
49886                 handler : function() {
49887                     // no animation..
49888                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
49889                 }
49890             }
49891         );
49892         var fel = Roo.get(footDisp.el);
49893         fel.addClass('x-editor-context');
49894         this.footDispWrap = fel; 
49895         this.footDispWrap.overflow  = 'hidden';
49896         
49897         this.footDisp = fel.createChild();
49898         this.footDispWrap.on('click', this.onContextClick, this)
49899         
49900         
49901     },
49902     // when the footer contect changes
49903     onContextClick : function (ev,dom)
49904     {
49905         ev.preventDefault();
49906         var  cn = dom.className;
49907         //Roo.log(cn);
49908         if (!cn.match(/x-ed-loc-/)) {
49909             return;
49910         }
49911         var n = cn.split('-').pop();
49912         var ans = this.footerEls;
49913         var sel = ans[n];
49914         
49915          // pick
49916         var range = this.editorcore.createRange();
49917         
49918         range.selectNodeContents(sel);
49919         //range.selectNode(sel);
49920         
49921         
49922         var selection = this.editorcore.getSelection();
49923         selection.removeAllRanges();
49924         selection.addRange(range);
49925         
49926         
49927         
49928         this.updateToolbar(null, null, sel);
49929         
49930         
49931     }
49932     
49933     
49934     
49935     
49936     
49937 });
49938
49939
49940
49941
49942
49943 /*
49944  * Based on:
49945  * Ext JS Library 1.1.1
49946  * Copyright(c) 2006-2007, Ext JS, LLC.
49947  *
49948  * Originally Released Under LGPL - original licence link has changed is not relivant.
49949  *
49950  * Fork - LGPL
49951  * <script type="text/javascript">
49952  */
49953  
49954 /**
49955  * @class Roo.form.BasicForm
49956  * @extends Roo.util.Observable
49957  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
49958  * @constructor
49959  * @param {String/HTMLElement/Roo.Element} el The form element or its id
49960  * @param {Object} config Configuration options
49961  */
49962 Roo.form.BasicForm = function(el, config){
49963     this.allItems = [];
49964     this.childForms = [];
49965     Roo.apply(this, config);
49966     /*
49967      * The Roo.form.Field items in this form.
49968      * @type MixedCollection
49969      */
49970      
49971      
49972     this.items = new Roo.util.MixedCollection(false, function(o){
49973         return o.id || (o.id = Roo.id());
49974     });
49975     this.addEvents({
49976         /**
49977          * @event beforeaction
49978          * Fires before any action is performed. Return false to cancel the action.
49979          * @param {Form} this
49980          * @param {Action} action The action to be performed
49981          */
49982         beforeaction: true,
49983         /**
49984          * @event actionfailed
49985          * Fires when an action fails.
49986          * @param {Form} this
49987          * @param {Action} action The action that failed
49988          */
49989         actionfailed : true,
49990         /**
49991          * @event actioncomplete
49992          * Fires when an action is completed.
49993          * @param {Form} this
49994          * @param {Action} action The action that completed
49995          */
49996         actioncomplete : true
49997     });
49998     if(el){
49999         this.initEl(el);
50000     }
50001     Roo.form.BasicForm.superclass.constructor.call(this);
50002     
50003     Roo.form.BasicForm.popover.apply();
50004 };
50005
50006 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
50007     /**
50008      * @cfg {String} method
50009      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
50010      */
50011     /**
50012      * @cfg {DataReader} reader
50013      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
50014      * This is optional as there is built-in support for processing JSON.
50015      */
50016     /**
50017      * @cfg {DataReader} errorReader
50018      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
50019      * This is completely optional as there is built-in support for processing JSON.
50020      */
50021     /**
50022      * @cfg {String} url
50023      * The URL to use for form actions if one isn't supplied in the action options.
50024      */
50025     /**
50026      * @cfg {Boolean} fileUpload
50027      * Set to true if this form is a file upload.
50028      */
50029      
50030     /**
50031      * @cfg {Object} baseParams
50032      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
50033      */
50034      /**
50035      
50036     /**
50037      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
50038      */
50039     timeout: 30,
50040
50041     // private
50042     activeAction : null,
50043
50044     /**
50045      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
50046      * or setValues() data instead of when the form was first created.
50047      */
50048     trackResetOnLoad : false,
50049     
50050     
50051     /**
50052      * childForms - used for multi-tab forms
50053      * @type {Array}
50054      */
50055     childForms : false,
50056     
50057     /**
50058      * allItems - full list of fields.
50059      * @type {Array}
50060      */
50061     allItems : false,
50062     
50063     /**
50064      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
50065      * element by passing it or its id or mask the form itself by passing in true.
50066      * @type Mixed
50067      */
50068     waitMsgTarget : false,
50069     
50070     /**
50071      * @type Boolean
50072      */
50073     disableMask : false,
50074     
50075     /**
50076      * @cfg {Boolean} errorMask (true|false) default false
50077      */
50078     errorMask : false,
50079     
50080     /**
50081      * @cfg {Number} maskOffset Default 100
50082      */
50083     maskOffset : 100,
50084
50085     // private
50086     initEl : function(el){
50087         this.el = Roo.get(el);
50088         this.id = this.el.id || Roo.id();
50089         this.el.on('submit', this.onSubmit, this);
50090         this.el.addClass('x-form');
50091     },
50092
50093     // private
50094     onSubmit : function(e){
50095         e.stopEvent();
50096     },
50097
50098     /**
50099      * Returns true if client-side validation on the form is successful.
50100      * @return Boolean
50101      */
50102     isValid : function(){
50103         var valid = true;
50104         var target = false;
50105         this.items.each(function(f){
50106             if(f.validate()){
50107                 return;
50108             }
50109             
50110             valid = false;
50111                 
50112             if(!target && f.el.isVisible(true)){
50113                 target = f;
50114             }
50115         });
50116         
50117         if(this.errorMask && !valid){
50118             Roo.form.BasicForm.popover.mask(this, target);
50119         }
50120         
50121         return valid;
50122     },
50123     /**
50124      * Returns array of invalid form fields.
50125      * @return Array
50126      */
50127     
50128     invalidFields : function()
50129     {
50130         var ret = [];
50131         this.items.each(function(f){
50132             if(f.validate()){
50133                 return;
50134             }
50135             ret.push(f);
50136             
50137         });
50138         
50139         return ret;
50140     },
50141     
50142     
50143     /**
50144      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
50145      * @return Boolean
50146      */
50147     isDirty : function(){
50148         var dirty = false;
50149         this.items.each(function(f){
50150            if(f.isDirty()){
50151                dirty = true;
50152                return false;
50153            }
50154         });
50155         return dirty;
50156     },
50157     
50158     /**
50159      * Returns true if any fields in this form have changed since their original load. (New version)
50160      * @return Boolean
50161      */
50162     
50163     hasChanged : function()
50164     {
50165         var dirty = false;
50166         this.items.each(function(f){
50167            if(f.hasChanged()){
50168                dirty = true;
50169                return false;
50170            }
50171         });
50172         return dirty;
50173         
50174     },
50175     /**
50176      * Resets all hasChanged to 'false' -
50177      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
50178      * So hasChanged storage is only to be used for this purpose
50179      * @return Boolean
50180      */
50181     resetHasChanged : function()
50182     {
50183         this.items.each(function(f){
50184            f.resetHasChanged();
50185         });
50186         
50187     },
50188     
50189     
50190     /**
50191      * Performs a predefined action (submit or load) or custom actions you define on this form.
50192      * @param {String} actionName The name of the action type
50193      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
50194      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
50195      * accept other config options):
50196      * <pre>
50197 Property          Type             Description
50198 ----------------  ---------------  ----------------------------------------------------------------------------------
50199 url               String           The url for the action (defaults to the form's url)
50200 method            String           The form method to use (defaults to the form's method, or POST if not defined)
50201 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
50202 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
50203                                    validate the form on the client (defaults to false)
50204      * </pre>
50205      * @return {BasicForm} this
50206      */
50207     doAction : function(action, options){
50208         if(typeof action == 'string'){
50209             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
50210         }
50211         if(this.fireEvent('beforeaction', this, action) !== false){
50212             this.beforeAction(action);
50213             action.run.defer(100, action);
50214         }
50215         return this;
50216     },
50217
50218     /**
50219      * Shortcut to do a submit action.
50220      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50221      * @return {BasicForm} this
50222      */
50223     submit : function(options){
50224         this.doAction('submit', options);
50225         return this;
50226     },
50227
50228     /**
50229      * Shortcut to do a load action.
50230      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50231      * @return {BasicForm} this
50232      */
50233     load : function(options){
50234         this.doAction('load', options);
50235         return this;
50236     },
50237
50238     /**
50239      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
50240      * @param {Record} record The record to edit
50241      * @return {BasicForm} this
50242      */
50243     updateRecord : function(record){
50244         record.beginEdit();
50245         var fs = record.fields;
50246         fs.each(function(f){
50247             var field = this.findField(f.name);
50248             if(field){
50249                 record.set(f.name, field.getValue());
50250             }
50251         }, this);
50252         record.endEdit();
50253         return this;
50254     },
50255
50256     /**
50257      * Loads an Roo.data.Record into this form.
50258      * @param {Record} record The record to load
50259      * @return {BasicForm} this
50260      */
50261     loadRecord : function(record){
50262         this.setValues(record.data);
50263         return this;
50264     },
50265
50266     // private
50267     beforeAction : function(action){
50268         var o = action.options;
50269         
50270         if(!this.disableMask) {
50271             if(this.waitMsgTarget === true){
50272                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
50273             }else if(this.waitMsgTarget){
50274                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
50275                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
50276             }else {
50277                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
50278             }
50279         }
50280         
50281          
50282     },
50283
50284     // private
50285     afterAction : function(action, success){
50286         this.activeAction = null;
50287         var o = action.options;
50288         
50289         if(!this.disableMask) {
50290             if(this.waitMsgTarget === true){
50291                 this.el.unmask();
50292             }else if(this.waitMsgTarget){
50293                 this.waitMsgTarget.unmask();
50294             }else{
50295                 Roo.MessageBox.updateProgress(1);
50296                 Roo.MessageBox.hide();
50297             }
50298         }
50299         
50300         if(success){
50301             if(o.reset){
50302                 this.reset();
50303             }
50304             Roo.callback(o.success, o.scope, [this, action]);
50305             this.fireEvent('actioncomplete', this, action);
50306             
50307         }else{
50308             
50309             // failure condition..
50310             // we have a scenario where updates need confirming.
50311             // eg. if a locking scenario exists..
50312             // we look for { errors : { needs_confirm : true }} in the response.
50313             if (
50314                 (typeof(action.result) != 'undefined')  &&
50315                 (typeof(action.result.errors) != 'undefined')  &&
50316                 (typeof(action.result.errors.needs_confirm) != 'undefined')
50317            ){
50318                 var _t = this;
50319                 Roo.MessageBox.confirm(
50320                     "Change requires confirmation",
50321                     action.result.errorMsg,
50322                     function(r) {
50323                         if (r != 'yes') {
50324                             return;
50325                         }
50326                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
50327                     }
50328                     
50329                 );
50330                 
50331                 
50332                 
50333                 return;
50334             }
50335             
50336             Roo.callback(o.failure, o.scope, [this, action]);
50337             // show an error message if no failed handler is set..
50338             if (!this.hasListener('actionfailed')) {
50339                 Roo.MessageBox.alert("Error",
50340                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
50341                         action.result.errorMsg :
50342                         "Saving Failed, please check your entries or try again"
50343                 );
50344             }
50345             
50346             this.fireEvent('actionfailed', this, action);
50347         }
50348         
50349     },
50350
50351     /**
50352      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
50353      * @param {String} id The value to search for
50354      * @return Field
50355      */
50356     findField : function(id){
50357         var field = this.items.get(id);
50358         if(!field){
50359             this.items.each(function(f){
50360                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
50361                     field = f;
50362                     return false;
50363                 }
50364             });
50365         }
50366         return field || null;
50367     },
50368
50369     /**
50370      * Add a secondary form to this one, 
50371      * Used to provide tabbed forms. One form is primary, with hidden values 
50372      * which mirror the elements from the other forms.
50373      * 
50374      * @param {Roo.form.Form} form to add.
50375      * 
50376      */
50377     addForm : function(form)
50378     {
50379        
50380         if (this.childForms.indexOf(form) > -1) {
50381             // already added..
50382             return;
50383         }
50384         this.childForms.push(form);
50385         var n = '';
50386         Roo.each(form.allItems, function (fe) {
50387             
50388             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
50389             if (this.findField(n)) { // already added..
50390                 return;
50391             }
50392             var add = new Roo.form.Hidden({
50393                 name : n
50394             });
50395             add.render(this.el);
50396             
50397             this.add( add );
50398         }, this);
50399         
50400     },
50401     /**
50402      * Mark fields in this form invalid in bulk.
50403      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
50404      * @return {BasicForm} this
50405      */
50406     markInvalid : function(errors){
50407         if(errors instanceof Array){
50408             for(var i = 0, len = errors.length; i < len; i++){
50409                 var fieldError = errors[i];
50410                 var f = this.findField(fieldError.id);
50411                 if(f){
50412                     f.markInvalid(fieldError.msg);
50413                 }
50414             }
50415         }else{
50416             var field, id;
50417             for(id in errors){
50418                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
50419                     field.markInvalid(errors[id]);
50420                 }
50421             }
50422         }
50423         Roo.each(this.childForms || [], function (f) {
50424             f.markInvalid(errors);
50425         });
50426         
50427         return this;
50428     },
50429
50430     /**
50431      * Set values for fields in this form in bulk.
50432      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
50433      * @return {BasicForm} this
50434      */
50435     setValues : function(values){
50436         if(values instanceof Array){ // array of objects
50437             for(var i = 0, len = values.length; i < len; i++){
50438                 var v = values[i];
50439                 var f = this.findField(v.id);
50440                 if(f){
50441                     f.setValue(v.value);
50442                     if(this.trackResetOnLoad){
50443                         f.originalValue = f.getValue();
50444                     }
50445                 }
50446             }
50447         }else{ // object hash
50448             var field, id;
50449             for(id in values){
50450                 if(typeof values[id] != 'function' && (field = this.findField(id))){
50451                     
50452                     if (field.setFromData && 
50453                         field.valueField && 
50454                         field.displayField &&
50455                         // combos' with local stores can 
50456                         // be queried via setValue()
50457                         // to set their value..
50458                         (field.store && !field.store.isLocal)
50459                         ) {
50460                         // it's a combo
50461                         var sd = { };
50462                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
50463                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
50464                         field.setFromData(sd);
50465                         
50466                     } else {
50467                         field.setValue(values[id]);
50468                     }
50469                     
50470                     
50471                     if(this.trackResetOnLoad){
50472                         field.originalValue = field.getValue();
50473                     }
50474                 }
50475             }
50476         }
50477         this.resetHasChanged();
50478         
50479         
50480         Roo.each(this.childForms || [], function (f) {
50481             f.setValues(values);
50482             f.resetHasChanged();
50483         });
50484                 
50485         return this;
50486     },
50487  
50488     /**
50489      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
50490      * they are returned as an array.
50491      * @param {Boolean} asString
50492      * @return {Object}
50493      */
50494     getValues : function(asString)
50495     {
50496         if (this.childForms) {
50497             // copy values from the child forms
50498             Roo.each(this.childForms, function (f) {
50499                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
50500             }, this);
50501         }
50502         
50503         // use formdata
50504         if (typeof(FormData) != 'undefined' && asString !== true) {
50505             // this relies on a 'recent' version of chrome apparently...
50506             try {
50507                 var fd = (new FormData(this.el.dom)).entries();
50508                 var ret = {};
50509                 var ent = fd.next();
50510                 while (!ent.done) {
50511                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
50512                     ent = fd.next();
50513                 };
50514                 return ret;
50515             } catch(e) {
50516                 
50517             }
50518             
50519         }
50520         
50521         
50522         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
50523         if(asString === true){
50524             return fs;
50525         }
50526         return Roo.urlDecode(fs);
50527     },
50528     
50529     /**
50530      * Returns the fields in this form as an object with key/value pairs. 
50531      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
50532      * @return {Object}
50533      */
50534     getFieldValues : function(with_hidden)
50535     {
50536         if (this.childForms) {
50537             // copy values from the child forms
50538             // should this call getFieldValues - probably not as we do not currently copy
50539             // hidden fields when we generate..
50540             Roo.each(this.childForms, function (f) {
50541                 this.setValues(f.getFieldValues());
50542             }, this);
50543         }
50544         
50545         var ret = {};
50546         this.items.each(function(f){
50547             
50548             if (f.readOnly) {
50549                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
50550                         // if a subform contains a copy of them.
50551                         // if you have subforms with the same editable data, you will need to copy the data back
50552                         // and forth.
50553             }
50554             
50555             if (!f.getName()) {
50556                 return;
50557             }
50558             var v = f.getValue();
50559             if (f.inputType =='radio') {
50560                 if (typeof(ret[f.getName()]) == 'undefined') {
50561                     ret[f.getName()] = ''; // empty..
50562                 }
50563                 
50564                 if (!f.el.dom.checked) {
50565                     return;
50566                     
50567                 }
50568                 v = f.el.dom.value;
50569                 
50570             }
50571             
50572             // not sure if this supported any more..
50573             if ((typeof(v) == 'object') && f.getRawValue) {
50574                 v = f.getRawValue() ; // dates..
50575             }
50576             // combo boxes where name != hiddenName...
50577             if (f.name != f.getName()) {
50578                 ret[f.name] = f.getRawValue();
50579             }
50580             ret[f.getName()] = v;
50581         });
50582         
50583         return ret;
50584     },
50585
50586     /**
50587      * Clears all invalid messages in this form.
50588      * @return {BasicForm} this
50589      */
50590     clearInvalid : function(){
50591         this.items.each(function(f){
50592            f.clearInvalid();
50593         });
50594         
50595         Roo.each(this.childForms || [], function (f) {
50596             f.clearInvalid();
50597         });
50598         
50599         
50600         return this;
50601     },
50602
50603     /**
50604      * Resets this form.
50605      * @return {BasicForm} this
50606      */
50607     reset : function(){
50608         this.items.each(function(f){
50609             f.reset();
50610         });
50611         
50612         Roo.each(this.childForms || [], function (f) {
50613             f.reset();
50614         });
50615         this.resetHasChanged();
50616         
50617         return this;
50618     },
50619
50620     /**
50621      * Add Roo.form components to this form.
50622      * @param {Field} field1
50623      * @param {Field} field2 (optional)
50624      * @param {Field} etc (optional)
50625      * @return {BasicForm} this
50626      */
50627     add : function(){
50628         this.items.addAll(Array.prototype.slice.call(arguments, 0));
50629         return this;
50630     },
50631
50632
50633     /**
50634      * Removes a field from the items collection (does NOT remove its markup).
50635      * @param {Field} field
50636      * @return {BasicForm} this
50637      */
50638     remove : function(field){
50639         this.items.remove(field);
50640         return this;
50641     },
50642
50643     /**
50644      * Looks at the fields in this form, checks them for an id attribute,
50645      * and calls applyTo on the existing dom element with that id.
50646      * @return {BasicForm} this
50647      */
50648     render : function(){
50649         this.items.each(function(f){
50650             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
50651                 f.applyTo(f.id);
50652             }
50653         });
50654         return this;
50655     },
50656
50657     /**
50658      * Calls {@link Ext#apply} for all fields in this form with the passed object.
50659      * @param {Object} values
50660      * @return {BasicForm} this
50661      */
50662     applyToFields : function(o){
50663         this.items.each(function(f){
50664            Roo.apply(f, o);
50665         });
50666         return this;
50667     },
50668
50669     /**
50670      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
50671      * @param {Object} values
50672      * @return {BasicForm} this
50673      */
50674     applyIfToFields : function(o){
50675         this.items.each(function(f){
50676            Roo.applyIf(f, o);
50677         });
50678         return this;
50679     }
50680 });
50681
50682 // back compat
50683 Roo.BasicForm = Roo.form.BasicForm;
50684
50685 Roo.apply(Roo.form.BasicForm, {
50686     
50687     popover : {
50688         
50689         padding : 5,
50690         
50691         isApplied : false,
50692         
50693         isMasked : false,
50694         
50695         form : false,
50696         
50697         target : false,
50698         
50699         intervalID : false,
50700         
50701         maskEl : false,
50702         
50703         apply : function()
50704         {
50705             if(this.isApplied){
50706                 return;
50707             }
50708             
50709             this.maskEl = {
50710                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
50711                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
50712                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
50713                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
50714             };
50715             
50716             this.maskEl.top.enableDisplayMode("block");
50717             this.maskEl.left.enableDisplayMode("block");
50718             this.maskEl.bottom.enableDisplayMode("block");
50719             this.maskEl.right.enableDisplayMode("block");
50720             
50721             Roo.get(document.body).on('click', function(){
50722                 this.unmask();
50723             }, this);
50724             
50725             Roo.get(document.body).on('touchstart', function(){
50726                 this.unmask();
50727             }, this);
50728             
50729             this.isApplied = true
50730         },
50731         
50732         mask : function(form, target)
50733         {
50734             this.form = form;
50735             
50736             this.target = target;
50737             
50738             if(!this.form.errorMask || !target.el){
50739                 return;
50740             }
50741             
50742             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
50743             
50744             var ot = this.target.el.calcOffsetsTo(scrollable);
50745             
50746             var scrollTo = ot[1] - this.form.maskOffset;
50747             
50748             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
50749             
50750             scrollable.scrollTo('top', scrollTo);
50751             
50752             var el = this.target.wrap || this.target.el;
50753             
50754             var box = el.getBox();
50755             
50756             this.maskEl.top.setStyle('position', 'absolute');
50757             this.maskEl.top.setStyle('z-index', 10000);
50758             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
50759             this.maskEl.top.setLeft(0);
50760             this.maskEl.top.setTop(0);
50761             this.maskEl.top.show();
50762             
50763             this.maskEl.left.setStyle('position', 'absolute');
50764             this.maskEl.left.setStyle('z-index', 10000);
50765             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
50766             this.maskEl.left.setLeft(0);
50767             this.maskEl.left.setTop(box.y - this.padding);
50768             this.maskEl.left.show();
50769
50770             this.maskEl.bottom.setStyle('position', 'absolute');
50771             this.maskEl.bottom.setStyle('z-index', 10000);
50772             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
50773             this.maskEl.bottom.setLeft(0);
50774             this.maskEl.bottom.setTop(box.bottom + this.padding);
50775             this.maskEl.bottom.show();
50776
50777             this.maskEl.right.setStyle('position', 'absolute');
50778             this.maskEl.right.setStyle('z-index', 10000);
50779             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
50780             this.maskEl.right.setLeft(box.right + this.padding);
50781             this.maskEl.right.setTop(box.y - this.padding);
50782             this.maskEl.right.show();
50783
50784             this.intervalID = window.setInterval(function() {
50785                 Roo.form.BasicForm.popover.unmask();
50786             }, 10000);
50787
50788             window.onwheel = function(){ return false;};
50789             
50790             (function(){ this.isMasked = true; }).defer(500, this);
50791             
50792         },
50793         
50794         unmask : function()
50795         {
50796             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
50797                 return;
50798             }
50799             
50800             this.maskEl.top.setStyle('position', 'absolute');
50801             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
50802             this.maskEl.top.hide();
50803
50804             this.maskEl.left.setStyle('position', 'absolute');
50805             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
50806             this.maskEl.left.hide();
50807
50808             this.maskEl.bottom.setStyle('position', 'absolute');
50809             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
50810             this.maskEl.bottom.hide();
50811
50812             this.maskEl.right.setStyle('position', 'absolute');
50813             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
50814             this.maskEl.right.hide();
50815             
50816             window.onwheel = function(){ return true;};
50817             
50818             if(this.intervalID){
50819                 window.clearInterval(this.intervalID);
50820                 this.intervalID = false;
50821             }
50822             
50823             this.isMasked = false;
50824             
50825         }
50826         
50827     }
50828     
50829 });/*
50830  * Based on:
50831  * Ext JS Library 1.1.1
50832  * Copyright(c) 2006-2007, Ext JS, LLC.
50833  *
50834  * Originally Released Under LGPL - original licence link has changed is not relivant.
50835  *
50836  * Fork - LGPL
50837  * <script type="text/javascript">
50838  */
50839
50840 /**
50841  * @class Roo.form.Form
50842  * @extends Roo.form.BasicForm
50843  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50844  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
50845  * @constructor
50846  * @param {Object} config Configuration options
50847  */
50848 Roo.form.Form = function(config){
50849     var xitems =  [];
50850     if (config.items) {
50851         xitems = config.items;
50852         delete config.items;
50853     }
50854    
50855     
50856     Roo.form.Form.superclass.constructor.call(this, null, config);
50857     this.url = this.url || this.action;
50858     if(!this.root){
50859         this.root = new Roo.form.Layout(Roo.applyIf({
50860             id: Roo.id()
50861         }, config));
50862     }
50863     this.active = this.root;
50864     /**
50865      * Array of all the buttons that have been added to this form via {@link addButton}
50866      * @type Array
50867      */
50868     this.buttons = [];
50869     this.allItems = [];
50870     this.addEvents({
50871         /**
50872          * @event clientvalidation
50873          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
50874          * @param {Form} this
50875          * @param {Boolean} valid true if the form has passed client-side validation
50876          */
50877         clientvalidation: true,
50878         /**
50879          * @event rendered
50880          * Fires when the form is rendered
50881          * @param {Roo.form.Form} form
50882          */
50883         rendered : true
50884     });
50885     
50886     if (this.progressUrl) {
50887             // push a hidden field onto the list of fields..
50888             this.addxtype( {
50889                     xns: Roo.form, 
50890                     xtype : 'Hidden', 
50891                     name : 'UPLOAD_IDENTIFIER' 
50892             });
50893         }
50894         
50895     
50896     Roo.each(xitems, this.addxtype, this);
50897     
50898 };
50899
50900 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
50901      /**
50902      * @cfg {Roo.Button} buttons[] buttons at bottom of form
50903      */
50904     
50905     /**
50906      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
50907      */
50908     /**
50909      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
50910      */
50911     /**
50912      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
50913      */
50914     buttonAlign:'center',
50915
50916     /**
50917      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
50918      */
50919     minButtonWidth:75,
50920
50921     /**
50922      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
50923      * This property cascades to child containers if not set.
50924      */
50925     labelAlign:'left',
50926
50927     /**
50928      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
50929      * fires a looping event with that state. This is required to bind buttons to the valid
50930      * state using the config value formBind:true on the button.
50931      */
50932     monitorValid : false,
50933
50934     /**
50935      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
50936      */
50937     monitorPoll : 200,
50938     
50939     /**
50940      * @cfg {String} progressUrl - Url to return progress data 
50941      */
50942     
50943     progressUrl : false,
50944     /**
50945      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
50946      * sending a formdata with extra parameters - eg uploaded elements.
50947      */
50948     
50949     formData : false,
50950     
50951     /**
50952      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
50953      * fields are added and the column is closed. If no fields are passed the column remains open
50954      * until end() is called.
50955      * @param {Object} config The config to pass to the column
50956      * @param {Field} field1 (optional)
50957      * @param {Field} field2 (optional)
50958      * @param {Field} etc (optional)
50959      * @return Column The column container object
50960      */
50961     column : function(c){
50962         var col = new Roo.form.Column(c);
50963         this.start(col);
50964         if(arguments.length > 1){ // duplicate code required because of Opera
50965             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50966             this.end();
50967         }
50968         return col;
50969     },
50970
50971     /**
50972      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
50973      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
50974      * until end() is called.
50975      * @param {Object} config The config to pass to the fieldset
50976      * @param {Field} field1 (optional)
50977      * @param {Field} field2 (optional)
50978      * @param {Field} etc (optional)
50979      * @return FieldSet The fieldset container object
50980      */
50981     fieldset : function(c){
50982         var fs = new Roo.form.FieldSet(c);
50983         this.start(fs);
50984         if(arguments.length > 1){ // duplicate code required because of Opera
50985             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50986             this.end();
50987         }
50988         return fs;
50989     },
50990
50991     /**
50992      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
50993      * fields are added and the container is closed. If no fields are passed the container remains open
50994      * until end() is called.
50995      * @param {Object} config The config to pass to the Layout
50996      * @param {Field} field1 (optional)
50997      * @param {Field} field2 (optional)
50998      * @param {Field} etc (optional)
50999      * @return Layout The container object
51000      */
51001     container : function(c){
51002         var l = new Roo.form.Layout(c);
51003         this.start(l);
51004         if(arguments.length > 1){ // duplicate code required because of Opera
51005             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51006             this.end();
51007         }
51008         return l;
51009     },
51010
51011     /**
51012      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
51013      * @param {Object} container A Roo.form.Layout or subclass of Layout
51014      * @return {Form} this
51015      */
51016     start : function(c){
51017         // cascade label info
51018         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
51019         this.active.stack.push(c);
51020         c.ownerCt = this.active;
51021         this.active = c;
51022         return this;
51023     },
51024
51025     /**
51026      * Closes the current open container
51027      * @return {Form} this
51028      */
51029     end : function(){
51030         if(this.active == this.root){
51031             return this;
51032         }
51033         this.active = this.active.ownerCt;
51034         return this;
51035     },
51036
51037     /**
51038      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
51039      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
51040      * as the label of the field.
51041      * @param {Field} field1
51042      * @param {Field} field2 (optional)
51043      * @param {Field} etc. (optional)
51044      * @return {Form} this
51045      */
51046     add : function(){
51047         this.active.stack.push.apply(this.active.stack, arguments);
51048         this.allItems.push.apply(this.allItems,arguments);
51049         var r = [];
51050         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
51051             if(a[i].isFormField){
51052                 r.push(a[i]);
51053             }
51054         }
51055         if(r.length > 0){
51056             Roo.form.Form.superclass.add.apply(this, r);
51057         }
51058         return this;
51059     },
51060     
51061
51062     
51063     
51064     
51065      /**
51066      * Find any element that has been added to a form, using it's ID or name
51067      * This can include framesets, columns etc. along with regular fields..
51068      * @param {String} id - id or name to find.
51069      
51070      * @return {Element} e - or false if nothing found.
51071      */
51072     findbyId : function(id)
51073     {
51074         var ret = false;
51075         if (!id) {
51076             return ret;
51077         }
51078         Roo.each(this.allItems, function(f){
51079             if (f.id == id || f.name == id ){
51080                 ret = f;
51081                 return false;
51082             }
51083         });
51084         return ret;
51085     },
51086
51087     
51088     
51089     /**
51090      * Render this form into the passed container. This should only be called once!
51091      * @param {String/HTMLElement/Element} container The element this component should be rendered into
51092      * @return {Form} this
51093      */
51094     render : function(ct)
51095     {
51096         
51097         
51098         
51099         ct = Roo.get(ct);
51100         var o = this.autoCreate || {
51101             tag: 'form',
51102             method : this.method || 'POST',
51103             id : this.id || Roo.id()
51104         };
51105         this.initEl(ct.createChild(o));
51106
51107         this.root.render(this.el);
51108         
51109        
51110              
51111         this.items.each(function(f){
51112             f.render('x-form-el-'+f.id);
51113         });
51114
51115         if(this.buttons.length > 0){
51116             // tables are required to maintain order and for correct IE layout
51117             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
51118                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
51119                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
51120             }}, null, true);
51121             var tr = tb.getElementsByTagName('tr')[0];
51122             for(var i = 0, len = this.buttons.length; i < len; i++) {
51123                 var b = this.buttons[i];
51124                 var td = document.createElement('td');
51125                 td.className = 'x-form-btn-td';
51126                 b.render(tr.appendChild(td));
51127             }
51128         }
51129         if(this.monitorValid){ // initialize after render
51130             this.startMonitoring();
51131         }
51132         this.fireEvent('rendered', this);
51133         return this;
51134     },
51135
51136     /**
51137      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
51138      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
51139      * object or a valid Roo.DomHelper element config
51140      * @param {Function} handler The function called when the button is clicked
51141      * @param {Object} scope (optional) The scope of the handler function
51142      * @return {Roo.Button}
51143      */
51144     addButton : function(config, handler, scope){
51145         var bc = {
51146             handler: handler,
51147             scope: scope,
51148             minWidth: this.minButtonWidth,
51149             hideParent:true
51150         };
51151         if(typeof config == "string"){
51152             bc.text = config;
51153         }else{
51154             Roo.apply(bc, config);
51155         }
51156         var btn = new Roo.Button(null, bc);
51157         this.buttons.push(btn);
51158         return btn;
51159     },
51160
51161      /**
51162      * Adds a series of form elements (using the xtype property as the factory method.
51163      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
51164      * @param {Object} config 
51165      */
51166     
51167     addxtype : function()
51168     {
51169         var ar = Array.prototype.slice.call(arguments, 0);
51170         var ret = false;
51171         for(var i = 0; i < ar.length; i++) {
51172             if (!ar[i]) {
51173                 continue; // skip -- if this happends something invalid got sent, we 
51174                 // should ignore it, as basically that interface element will not show up
51175                 // and that should be pretty obvious!!
51176             }
51177             
51178             if (Roo.form[ar[i].xtype]) {
51179                 ar[i].form = this;
51180                 var fe = Roo.factory(ar[i], Roo.form);
51181                 if (!ret) {
51182                     ret = fe;
51183                 }
51184                 fe.form = this;
51185                 if (fe.store) {
51186                     fe.store.form = this;
51187                 }
51188                 if (fe.isLayout) {  
51189                          
51190                     this.start(fe);
51191                     this.allItems.push(fe);
51192                     if (fe.items && fe.addxtype) {
51193                         fe.addxtype.apply(fe, fe.items);
51194                         delete fe.items;
51195                     }
51196                      this.end();
51197                     continue;
51198                 }
51199                 
51200                 
51201                  
51202                 this.add(fe);
51203               //  console.log('adding ' + ar[i].xtype);
51204             }
51205             if (ar[i].xtype == 'Button') {  
51206                 //console.log('adding button');
51207                 //console.log(ar[i]);
51208                 this.addButton(ar[i]);
51209                 this.allItems.push(fe);
51210                 continue;
51211             }
51212             
51213             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
51214                 alert('end is not supported on xtype any more, use items');
51215             //    this.end();
51216             //    //console.log('adding end');
51217             }
51218             
51219         }
51220         return ret;
51221     },
51222     
51223     /**
51224      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
51225      * option "monitorValid"
51226      */
51227     startMonitoring : function(){
51228         if(!this.bound){
51229             this.bound = true;
51230             Roo.TaskMgr.start({
51231                 run : this.bindHandler,
51232                 interval : this.monitorPoll || 200,
51233                 scope: this
51234             });
51235         }
51236     },
51237
51238     /**
51239      * Stops monitoring of the valid state of this form
51240      */
51241     stopMonitoring : function(){
51242         this.bound = false;
51243     },
51244
51245     // private
51246     bindHandler : function(){
51247         if(!this.bound){
51248             return false; // stops binding
51249         }
51250         var valid = true;
51251         this.items.each(function(f){
51252             if(!f.isValid(true)){
51253                 valid = false;
51254                 return false;
51255             }
51256         });
51257         for(var i = 0, len = this.buttons.length; i < len; i++){
51258             var btn = this.buttons[i];
51259             if(btn.formBind === true && btn.disabled === valid){
51260                 btn.setDisabled(!valid);
51261             }
51262         }
51263         this.fireEvent('clientvalidation', this, valid);
51264     }
51265     
51266     
51267     
51268     
51269     
51270     
51271     
51272     
51273 });
51274
51275
51276 // back compat
51277 Roo.Form = Roo.form.Form;
51278 /*
51279  * Based on:
51280  * Ext JS Library 1.1.1
51281  * Copyright(c) 2006-2007, Ext JS, LLC.
51282  *
51283  * Originally Released Under LGPL - original licence link has changed is not relivant.
51284  *
51285  * Fork - LGPL
51286  * <script type="text/javascript">
51287  */
51288
51289 // as we use this in bootstrap.
51290 Roo.namespace('Roo.form');
51291  /**
51292  * @class Roo.form.Action
51293  * Internal Class used to handle form actions
51294  * @constructor
51295  * @param {Roo.form.BasicForm} el The form element or its id
51296  * @param {Object} config Configuration options
51297  */
51298
51299  
51300  
51301 // define the action interface
51302 Roo.form.Action = function(form, options){
51303     this.form = form;
51304     this.options = options || {};
51305 };
51306 /**
51307  * Client Validation Failed
51308  * @const 
51309  */
51310 Roo.form.Action.CLIENT_INVALID = 'client';
51311 /**
51312  * Server Validation Failed
51313  * @const 
51314  */
51315 Roo.form.Action.SERVER_INVALID = 'server';
51316  /**
51317  * Connect to Server Failed
51318  * @const 
51319  */
51320 Roo.form.Action.CONNECT_FAILURE = 'connect';
51321 /**
51322  * Reading Data from Server Failed
51323  * @const 
51324  */
51325 Roo.form.Action.LOAD_FAILURE = 'load';
51326
51327 Roo.form.Action.prototype = {
51328     type : 'default',
51329     failureType : undefined,
51330     response : undefined,
51331     result : undefined,
51332
51333     // interface method
51334     run : function(options){
51335
51336     },
51337
51338     // interface method
51339     success : function(response){
51340
51341     },
51342
51343     // interface method
51344     handleResponse : function(response){
51345
51346     },
51347
51348     // default connection failure
51349     failure : function(response){
51350         
51351         this.response = response;
51352         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51353         this.form.afterAction(this, false);
51354     },
51355
51356     processResponse : function(response){
51357         this.response = response;
51358         if(!response.responseText){
51359             return true;
51360         }
51361         this.result = this.handleResponse(response);
51362         return this.result;
51363     },
51364
51365     // utility functions used internally
51366     getUrl : function(appendParams){
51367         var url = this.options.url || this.form.url || this.form.el.dom.action;
51368         if(appendParams){
51369             var p = this.getParams();
51370             if(p){
51371                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
51372             }
51373         }
51374         return url;
51375     },
51376
51377     getMethod : function(){
51378         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
51379     },
51380
51381     getParams : function(){
51382         var bp = this.form.baseParams;
51383         var p = this.options.params;
51384         if(p){
51385             if(typeof p == "object"){
51386                 p = Roo.urlEncode(Roo.applyIf(p, bp));
51387             }else if(typeof p == 'string' && bp){
51388                 p += '&' + Roo.urlEncode(bp);
51389             }
51390         }else if(bp){
51391             p = Roo.urlEncode(bp);
51392         }
51393         return p;
51394     },
51395
51396     createCallback : function(){
51397         return {
51398             success: this.success,
51399             failure: this.failure,
51400             scope: this,
51401             timeout: (this.form.timeout*1000),
51402             upload: this.form.fileUpload ? this.success : undefined
51403         };
51404     }
51405 };
51406
51407 Roo.form.Action.Submit = function(form, options){
51408     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
51409 };
51410
51411 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
51412     type : 'submit',
51413
51414     haveProgress : false,
51415     uploadComplete : false,
51416     
51417     // uploadProgress indicator.
51418     uploadProgress : function()
51419     {
51420         if (!this.form.progressUrl) {
51421             return;
51422         }
51423         
51424         if (!this.haveProgress) {
51425             Roo.MessageBox.progress("Uploading", "Uploading");
51426         }
51427         if (this.uploadComplete) {
51428            Roo.MessageBox.hide();
51429            return;
51430         }
51431         
51432         this.haveProgress = true;
51433    
51434         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
51435         
51436         var c = new Roo.data.Connection();
51437         c.request({
51438             url : this.form.progressUrl,
51439             params: {
51440                 id : uid
51441             },
51442             method: 'GET',
51443             success : function(req){
51444                //console.log(data);
51445                 var rdata = false;
51446                 var edata;
51447                 try  {
51448                    rdata = Roo.decode(req.responseText)
51449                 } catch (e) {
51450                     Roo.log("Invalid data from server..");
51451                     Roo.log(edata);
51452                     return;
51453                 }
51454                 if (!rdata || !rdata.success) {
51455                     Roo.log(rdata);
51456                     Roo.MessageBox.alert(Roo.encode(rdata));
51457                     return;
51458                 }
51459                 var data = rdata.data;
51460                 
51461                 if (this.uploadComplete) {
51462                    Roo.MessageBox.hide();
51463                    return;
51464                 }
51465                    
51466                 if (data){
51467                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
51468                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
51469                     );
51470                 }
51471                 this.uploadProgress.defer(2000,this);
51472             },
51473        
51474             failure: function(data) {
51475                 Roo.log('progress url failed ');
51476                 Roo.log(data);
51477             },
51478             scope : this
51479         });
51480            
51481     },
51482     
51483     
51484     run : function()
51485     {
51486         // run get Values on the form, so it syncs any secondary forms.
51487         this.form.getValues();
51488         
51489         var o = this.options;
51490         var method = this.getMethod();
51491         var isPost = method == 'POST';
51492         if(o.clientValidation === false || this.form.isValid()){
51493             
51494             if (this.form.progressUrl) {
51495                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
51496                     (new Date() * 1) + '' + Math.random());
51497                     
51498             } 
51499             
51500             
51501             Roo.Ajax.request(Roo.apply(this.createCallback(), {
51502                 form:this.form.el.dom,
51503                 url:this.getUrl(!isPost),
51504                 method: method,
51505                 params:isPost ? this.getParams() : null,
51506                 isUpload: this.form.fileUpload,
51507                 formData : this.form.formData
51508             }));
51509             
51510             this.uploadProgress();
51511
51512         }else if (o.clientValidation !== false){ // client validation failed
51513             this.failureType = Roo.form.Action.CLIENT_INVALID;
51514             this.form.afterAction(this, false);
51515         }
51516     },
51517
51518     success : function(response)
51519     {
51520         this.uploadComplete= true;
51521         if (this.haveProgress) {
51522             Roo.MessageBox.hide();
51523         }
51524         
51525         
51526         var result = this.processResponse(response);
51527         if(result === true || result.success){
51528             this.form.afterAction(this, true);
51529             return;
51530         }
51531         if(result.errors){
51532             this.form.markInvalid(result.errors);
51533             this.failureType = Roo.form.Action.SERVER_INVALID;
51534         }
51535         this.form.afterAction(this, false);
51536     },
51537     failure : function(response)
51538     {
51539         this.uploadComplete= true;
51540         if (this.haveProgress) {
51541             Roo.MessageBox.hide();
51542         }
51543         
51544         this.response = response;
51545         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51546         this.form.afterAction(this, false);
51547     },
51548     
51549     handleResponse : function(response){
51550         if(this.form.errorReader){
51551             var rs = this.form.errorReader.read(response);
51552             var errors = [];
51553             if(rs.records){
51554                 for(var i = 0, len = rs.records.length; i < len; i++) {
51555                     var r = rs.records[i];
51556                     errors[i] = r.data;
51557                 }
51558             }
51559             if(errors.length < 1){
51560                 errors = null;
51561             }
51562             return {
51563                 success : rs.success,
51564                 errors : errors
51565             };
51566         }
51567         var ret = false;
51568         try {
51569             ret = Roo.decode(response.responseText);
51570         } catch (e) {
51571             ret = {
51572                 success: false,
51573                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
51574                 errors : []
51575             };
51576         }
51577         return ret;
51578         
51579     }
51580 });
51581
51582
51583 Roo.form.Action.Load = function(form, options){
51584     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
51585     this.reader = this.form.reader;
51586 };
51587
51588 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
51589     type : 'load',
51590
51591     run : function(){
51592         
51593         Roo.Ajax.request(Roo.apply(
51594                 this.createCallback(), {
51595                     method:this.getMethod(),
51596                     url:this.getUrl(false),
51597                     params:this.getParams()
51598         }));
51599     },
51600
51601     success : function(response){
51602         
51603         var result = this.processResponse(response);
51604         if(result === true || !result.success || !result.data){
51605             this.failureType = Roo.form.Action.LOAD_FAILURE;
51606             this.form.afterAction(this, false);
51607             return;
51608         }
51609         this.form.clearInvalid();
51610         this.form.setValues(result.data);
51611         this.form.afterAction(this, true);
51612     },
51613
51614     handleResponse : function(response){
51615         if(this.form.reader){
51616             var rs = this.form.reader.read(response);
51617             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
51618             return {
51619                 success : rs.success,
51620                 data : data
51621             };
51622         }
51623         return Roo.decode(response.responseText);
51624     }
51625 });
51626
51627 Roo.form.Action.ACTION_TYPES = {
51628     'load' : Roo.form.Action.Load,
51629     'submit' : Roo.form.Action.Submit
51630 };/*
51631  * Based on:
51632  * Ext JS Library 1.1.1
51633  * Copyright(c) 2006-2007, Ext JS, LLC.
51634  *
51635  * Originally Released Under LGPL - original licence link has changed is not relivant.
51636  *
51637  * Fork - LGPL
51638  * <script type="text/javascript">
51639  */
51640  
51641 /**
51642  * @class Roo.form.Layout
51643  * @extends Roo.Component
51644  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
51645  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
51646  * @constructor
51647  * @param {Object} config Configuration options
51648  */
51649 Roo.form.Layout = function(config){
51650     var xitems = [];
51651     if (config.items) {
51652         xitems = config.items;
51653         delete config.items;
51654     }
51655     Roo.form.Layout.superclass.constructor.call(this, config);
51656     this.stack = [];
51657     Roo.each(xitems, this.addxtype, this);
51658      
51659 };
51660
51661 Roo.extend(Roo.form.Layout, Roo.Component, {
51662     /**
51663      * @cfg {String/Object} autoCreate
51664      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
51665      */
51666     /**
51667      * @cfg {String/Object/Function} style
51668      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
51669      * a function which returns such a specification.
51670      */
51671     /**
51672      * @cfg {String} labelAlign
51673      * Valid values are "left," "top" and "right" (defaults to "left")
51674      */
51675     /**
51676      * @cfg {Number} labelWidth
51677      * Fixed width in pixels of all field labels (defaults to undefined)
51678      */
51679     /**
51680      * @cfg {Boolean} clear
51681      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
51682      */
51683     clear : true,
51684     /**
51685      * @cfg {String} labelSeparator
51686      * The separator to use after field labels (defaults to ':')
51687      */
51688     labelSeparator : ':',
51689     /**
51690      * @cfg {Boolean} hideLabels
51691      * True to suppress the display of field labels in this layout (defaults to false)
51692      */
51693     hideLabels : false,
51694
51695     // private
51696     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
51697     
51698     isLayout : true,
51699     
51700     // private
51701     onRender : function(ct, position){
51702         if(this.el){ // from markup
51703             this.el = Roo.get(this.el);
51704         }else {  // generate
51705             var cfg = this.getAutoCreate();
51706             this.el = ct.createChild(cfg, position);
51707         }
51708         if(this.style){
51709             this.el.applyStyles(this.style);
51710         }
51711         if(this.labelAlign){
51712             this.el.addClass('x-form-label-'+this.labelAlign);
51713         }
51714         if(this.hideLabels){
51715             this.labelStyle = "display:none";
51716             this.elementStyle = "padding-left:0;";
51717         }else{
51718             if(typeof this.labelWidth == 'number'){
51719                 this.labelStyle = "width:"+this.labelWidth+"px;";
51720                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
51721             }
51722             if(this.labelAlign == 'top'){
51723                 this.labelStyle = "width:auto;";
51724                 this.elementStyle = "padding-left:0;";
51725             }
51726         }
51727         var stack = this.stack;
51728         var slen = stack.length;
51729         if(slen > 0){
51730             if(!this.fieldTpl){
51731                 var t = new Roo.Template(
51732                     '<div class="x-form-item {5}">',
51733                         '<label for="{0}" style="{2}">{1}{4}</label>',
51734                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
51735                         '</div>',
51736                     '</div><div class="x-form-clear-left"></div>'
51737                 );
51738                 t.disableFormats = true;
51739                 t.compile();
51740                 Roo.form.Layout.prototype.fieldTpl = t;
51741             }
51742             for(var i = 0; i < slen; i++) {
51743                 if(stack[i].isFormField){
51744                     this.renderField(stack[i]);
51745                 }else{
51746                     this.renderComponent(stack[i]);
51747                 }
51748             }
51749         }
51750         if(this.clear){
51751             this.el.createChild({cls:'x-form-clear'});
51752         }
51753     },
51754
51755     // private
51756     renderField : function(f){
51757         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
51758                f.id, //0
51759                f.fieldLabel, //1
51760                f.labelStyle||this.labelStyle||'', //2
51761                this.elementStyle||'', //3
51762                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
51763                f.itemCls||this.itemCls||''  //5
51764        ], true).getPrevSibling());
51765     },
51766
51767     // private
51768     renderComponent : function(c){
51769         c.render(c.isLayout ? this.el : this.el.createChild());    
51770     },
51771     /**
51772      * Adds a object form elements (using the xtype property as the factory method.)
51773      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
51774      * @param {Object} config 
51775      */
51776     addxtype : function(o)
51777     {
51778         // create the lement.
51779         o.form = this.form;
51780         var fe = Roo.factory(o, Roo.form);
51781         this.form.allItems.push(fe);
51782         this.stack.push(fe);
51783         
51784         if (fe.isFormField) {
51785             this.form.items.add(fe);
51786         }
51787          
51788         return fe;
51789     }
51790 });
51791
51792 /**
51793  * @class Roo.form.Column
51794  * @extends Roo.form.Layout
51795  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
51796  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
51797  * @constructor
51798  * @param {Object} config Configuration options
51799  */
51800 Roo.form.Column = function(config){
51801     Roo.form.Column.superclass.constructor.call(this, config);
51802 };
51803
51804 Roo.extend(Roo.form.Column, Roo.form.Layout, {
51805     /**
51806      * @cfg {Number/String} width
51807      * The fixed width of the column in pixels or CSS value (defaults to "auto")
51808      */
51809     /**
51810      * @cfg {String/Object} autoCreate
51811      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
51812      */
51813
51814     // private
51815     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
51816
51817     // private
51818     onRender : function(ct, position){
51819         Roo.form.Column.superclass.onRender.call(this, ct, position);
51820         if(this.width){
51821             this.el.setWidth(this.width);
51822         }
51823     }
51824 });
51825
51826
51827 /**
51828  * @class Roo.form.Row
51829  * @extends Roo.form.Layout
51830  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
51831  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
51832  * @constructor
51833  * @param {Object} config Configuration options
51834  */
51835
51836  
51837 Roo.form.Row = function(config){
51838     Roo.form.Row.superclass.constructor.call(this, config);
51839 };
51840  
51841 Roo.extend(Roo.form.Row, Roo.form.Layout, {
51842       /**
51843      * @cfg {Number/String} width
51844      * The fixed width of the column in pixels or CSS value (defaults to "auto")
51845      */
51846     /**
51847      * @cfg {Number/String} height
51848      * The fixed height of the column in pixels or CSS value (defaults to "auto")
51849      */
51850     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
51851     
51852     padWidth : 20,
51853     // private
51854     onRender : function(ct, position){
51855         //console.log('row render');
51856         if(!this.rowTpl){
51857             var t = new Roo.Template(
51858                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
51859                     '<label for="{0}" style="{2}">{1}{4}</label>',
51860                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
51861                     '</div>',
51862                 '</div>'
51863             );
51864             t.disableFormats = true;
51865             t.compile();
51866             Roo.form.Layout.prototype.rowTpl = t;
51867         }
51868         this.fieldTpl = this.rowTpl;
51869         
51870         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
51871         var labelWidth = 100;
51872         
51873         if ((this.labelAlign != 'top')) {
51874             if (typeof this.labelWidth == 'number') {
51875                 labelWidth = this.labelWidth
51876             }
51877             this.padWidth =  20 + labelWidth;
51878             
51879         }
51880         
51881         Roo.form.Column.superclass.onRender.call(this, ct, position);
51882         if(this.width){
51883             this.el.setWidth(this.width);
51884         }
51885         if(this.height){
51886             this.el.setHeight(this.height);
51887         }
51888     },
51889     
51890     // private
51891     renderField : function(f){
51892         f.fieldEl = this.fieldTpl.append(this.el, [
51893                f.id, f.fieldLabel,
51894                f.labelStyle||this.labelStyle||'',
51895                this.elementStyle||'',
51896                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
51897                f.itemCls||this.itemCls||'',
51898                f.width ? f.width + this.padWidth : 160 + this.padWidth
51899        ],true);
51900     }
51901 });
51902  
51903
51904 /**
51905  * @class Roo.form.FieldSet
51906  * @extends Roo.form.Layout
51907  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51908  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
51909  * @constructor
51910  * @param {Object} config Configuration options
51911  */
51912 Roo.form.FieldSet = function(config){
51913     Roo.form.FieldSet.superclass.constructor.call(this, config);
51914 };
51915
51916 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
51917     /**
51918      * @cfg {String} legend
51919      * The text to display as the legend for the FieldSet (defaults to '')
51920      */
51921     /**
51922      * @cfg {String/Object} autoCreate
51923      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
51924      */
51925
51926     // private
51927     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
51928
51929     // private
51930     onRender : function(ct, position){
51931         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
51932         if(this.legend){
51933             this.setLegend(this.legend);
51934         }
51935     },
51936
51937     // private
51938     setLegend : function(text){
51939         if(this.rendered){
51940             this.el.child('legend').update(text);
51941         }
51942     }
51943 });/*
51944  * Based on:
51945  * Ext JS Library 1.1.1
51946  * Copyright(c) 2006-2007, Ext JS, LLC.
51947  *
51948  * Originally Released Under LGPL - original licence link has changed is not relivant.
51949  *
51950  * Fork - LGPL
51951  * <script type="text/javascript">
51952  */
51953 /**
51954  * @class Roo.form.VTypes
51955  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
51956  * @static
51957  */
51958 Roo.form.VTypes = function(){
51959     // closure these in so they are only created once.
51960     var alpha = /^[a-zA-Z_]+$/;
51961     var alphanum = /^[a-zA-Z0-9_]+$/;
51962     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
51963     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
51964
51965     // All these messages and functions are configurable
51966     return {
51967         /**
51968          * The function used to validate email addresses
51969          * @param {String} value The email address
51970          */
51971         'email' : function(v){
51972             return email.test(v);
51973         },
51974         /**
51975          * The error text to display when the email validation function returns false
51976          * @type String
51977          */
51978         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
51979         /**
51980          * The keystroke filter mask to be applied on email input
51981          * @type RegExp
51982          */
51983         'emailMask' : /[a-z0-9_\.\-@]/i,
51984
51985         /**
51986          * The function used to validate URLs
51987          * @param {String} value The URL
51988          */
51989         'url' : function(v){
51990             return url.test(v);
51991         },
51992         /**
51993          * The error text to display when the url validation function returns false
51994          * @type String
51995          */
51996         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
51997         
51998         /**
51999          * The function used to validate alpha values
52000          * @param {String} value The value
52001          */
52002         'alpha' : function(v){
52003             return alpha.test(v);
52004         },
52005         /**
52006          * The error text to display when the alpha validation function returns false
52007          * @type String
52008          */
52009         'alphaText' : 'This field should only contain letters and _',
52010         /**
52011          * The keystroke filter mask to be applied on alpha input
52012          * @type RegExp
52013          */
52014         'alphaMask' : /[a-z_]/i,
52015
52016         /**
52017          * The function used to validate alphanumeric values
52018          * @param {String} value The value
52019          */
52020         'alphanum' : function(v){
52021             return alphanum.test(v);
52022         },
52023         /**
52024          * The error text to display when the alphanumeric validation function returns false
52025          * @type String
52026          */
52027         'alphanumText' : 'This field should only contain letters, numbers and _',
52028         /**
52029          * The keystroke filter mask to be applied on alphanumeric input
52030          * @type RegExp
52031          */
52032         'alphanumMask' : /[a-z0-9_]/i
52033     };
52034 }();//<script type="text/javascript">
52035
52036 /**
52037  * @class Roo.form.FCKeditor
52038  * @extends Roo.form.TextArea
52039  * Wrapper around the FCKEditor http://www.fckeditor.net
52040  * @constructor
52041  * Creates a new FCKeditor
52042  * @param {Object} config Configuration options
52043  */
52044 Roo.form.FCKeditor = function(config){
52045     Roo.form.FCKeditor.superclass.constructor.call(this, config);
52046     this.addEvents({
52047          /**
52048          * @event editorinit
52049          * Fired when the editor is initialized - you can add extra handlers here..
52050          * @param {FCKeditor} this
52051          * @param {Object} the FCK object.
52052          */
52053         editorinit : true
52054     });
52055     
52056     
52057 };
52058 Roo.form.FCKeditor.editors = { };
52059 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
52060 {
52061     //defaultAutoCreate : {
52062     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
52063     //},
52064     // private
52065     /**
52066      * @cfg {Object} fck options - see fck manual for details.
52067      */
52068     fckconfig : false,
52069     
52070     /**
52071      * @cfg {Object} fck toolbar set (Basic or Default)
52072      */
52073     toolbarSet : 'Basic',
52074     /**
52075      * @cfg {Object} fck BasePath
52076      */ 
52077     basePath : '/fckeditor/',
52078     
52079     
52080     frame : false,
52081     
52082     value : '',
52083     
52084    
52085     onRender : function(ct, position)
52086     {
52087         if(!this.el){
52088             this.defaultAutoCreate = {
52089                 tag: "textarea",
52090                 style:"width:300px;height:60px;",
52091                 autocomplete: "new-password"
52092             };
52093         }
52094         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
52095         /*
52096         if(this.grow){
52097             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
52098             if(this.preventScrollbars){
52099                 this.el.setStyle("overflow", "hidden");
52100             }
52101             this.el.setHeight(this.growMin);
52102         }
52103         */
52104         //console.log('onrender' + this.getId() );
52105         Roo.form.FCKeditor.editors[this.getId()] = this;
52106          
52107
52108         this.replaceTextarea() ;
52109         
52110     },
52111     
52112     getEditor : function() {
52113         return this.fckEditor;
52114     },
52115     /**
52116      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
52117      * @param {Mixed} value The value to set
52118      */
52119     
52120     
52121     setValue : function(value)
52122     {
52123         //console.log('setValue: ' + value);
52124         
52125         if(typeof(value) == 'undefined') { // not sure why this is happending...
52126             return;
52127         }
52128         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52129         
52130         //if(!this.el || !this.getEditor()) {
52131         //    this.value = value;
52132             //this.setValue.defer(100,this,[value]);    
52133         //    return;
52134         //} 
52135         
52136         if(!this.getEditor()) {
52137             return;
52138         }
52139         
52140         this.getEditor().SetData(value);
52141         
52142         //
52143
52144     },
52145
52146     /**
52147      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
52148      * @return {Mixed} value The field value
52149      */
52150     getValue : function()
52151     {
52152         
52153         if (this.frame && this.frame.dom.style.display == 'none') {
52154             return Roo.form.FCKeditor.superclass.getValue.call(this);
52155         }
52156         
52157         if(!this.el || !this.getEditor()) {
52158            
52159            // this.getValue.defer(100,this); 
52160             return this.value;
52161         }
52162        
52163         
52164         var value=this.getEditor().GetData();
52165         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52166         return Roo.form.FCKeditor.superclass.getValue.call(this);
52167         
52168
52169     },
52170
52171     /**
52172      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
52173      * @return {Mixed} value The field value
52174      */
52175     getRawValue : function()
52176     {
52177         if (this.frame && this.frame.dom.style.display == 'none') {
52178             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52179         }
52180         
52181         if(!this.el || !this.getEditor()) {
52182             //this.getRawValue.defer(100,this); 
52183             return this.value;
52184             return;
52185         }
52186         
52187         
52188         
52189         var value=this.getEditor().GetData();
52190         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
52191         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52192          
52193     },
52194     
52195     setSize : function(w,h) {
52196         
52197         
52198         
52199         //if (this.frame && this.frame.dom.style.display == 'none') {
52200         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52201         //    return;
52202         //}
52203         //if(!this.el || !this.getEditor()) {
52204         //    this.setSize.defer(100,this, [w,h]); 
52205         //    return;
52206         //}
52207         
52208         
52209         
52210         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52211         
52212         this.frame.dom.setAttribute('width', w);
52213         this.frame.dom.setAttribute('height', h);
52214         this.frame.setSize(w,h);
52215         
52216     },
52217     
52218     toggleSourceEdit : function(value) {
52219         
52220       
52221          
52222         this.el.dom.style.display = value ? '' : 'none';
52223         this.frame.dom.style.display = value ?  'none' : '';
52224         
52225     },
52226     
52227     
52228     focus: function(tag)
52229     {
52230         if (this.frame.dom.style.display == 'none') {
52231             return Roo.form.FCKeditor.superclass.focus.call(this);
52232         }
52233         if(!this.el || !this.getEditor()) {
52234             this.focus.defer(100,this, [tag]); 
52235             return;
52236         }
52237         
52238         
52239         
52240         
52241         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
52242         this.getEditor().Focus();
52243         if (tgs.length) {
52244             if (!this.getEditor().Selection.GetSelection()) {
52245                 this.focus.defer(100,this, [tag]); 
52246                 return;
52247             }
52248             
52249             
52250             var r = this.getEditor().EditorDocument.createRange();
52251             r.setStart(tgs[0],0);
52252             r.setEnd(tgs[0],0);
52253             this.getEditor().Selection.GetSelection().removeAllRanges();
52254             this.getEditor().Selection.GetSelection().addRange(r);
52255             this.getEditor().Focus();
52256         }
52257         
52258     },
52259     
52260     
52261     
52262     replaceTextarea : function()
52263     {
52264         if ( document.getElementById( this.getId() + '___Frame' ) ) {
52265             return ;
52266         }
52267         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
52268         //{
52269             // We must check the elements firstly using the Id and then the name.
52270         var oTextarea = document.getElementById( this.getId() );
52271         
52272         var colElementsByName = document.getElementsByName( this.getId() ) ;
52273          
52274         oTextarea.style.display = 'none' ;
52275
52276         if ( oTextarea.tabIndex ) {            
52277             this.TabIndex = oTextarea.tabIndex ;
52278         }
52279         
52280         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
52281         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
52282         this.frame = Roo.get(this.getId() + '___Frame')
52283     },
52284     
52285     _getConfigHtml : function()
52286     {
52287         var sConfig = '' ;
52288
52289         for ( var o in this.fckconfig ) {
52290             sConfig += sConfig.length > 0  ? '&amp;' : '';
52291             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
52292         }
52293
52294         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
52295     },
52296     
52297     
52298     _getIFrameHtml : function()
52299     {
52300         var sFile = 'fckeditor.html' ;
52301         /* no idea what this is about..
52302         try
52303         {
52304             if ( (/fcksource=true/i).test( window.top.location.search ) )
52305                 sFile = 'fckeditor.original.html' ;
52306         }
52307         catch (e) { 
52308         */
52309
52310         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
52311         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
52312         
52313         
52314         var html = '<iframe id="' + this.getId() +
52315             '___Frame" src="' + sLink +
52316             '" width="' + this.width +
52317             '" height="' + this.height + '"' +
52318             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
52319             ' frameborder="0" scrolling="no"></iframe>' ;
52320
52321         return html ;
52322     },
52323     
52324     _insertHtmlBefore : function( html, element )
52325     {
52326         if ( element.insertAdjacentHTML )       {
52327             // IE
52328             element.insertAdjacentHTML( 'beforeBegin', html ) ;
52329         } else { // Gecko
52330             var oRange = document.createRange() ;
52331             oRange.setStartBefore( element ) ;
52332             var oFragment = oRange.createContextualFragment( html );
52333             element.parentNode.insertBefore( oFragment, element ) ;
52334         }
52335     }
52336     
52337     
52338   
52339     
52340     
52341     
52342     
52343
52344 });
52345
52346 //Roo.reg('fckeditor', Roo.form.FCKeditor);
52347
52348 function FCKeditor_OnComplete(editorInstance){
52349     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
52350     f.fckEditor = editorInstance;
52351     //console.log("loaded");
52352     f.fireEvent('editorinit', f, editorInstance);
52353
52354   
52355
52356  
52357
52358
52359
52360
52361
52362
52363
52364
52365
52366
52367
52368
52369
52370
52371
52372 //<script type="text/javascript">
52373 /**
52374  * @class Roo.form.GridField
52375  * @extends Roo.form.Field
52376  * Embed a grid (or editable grid into a form)
52377  * STATUS ALPHA
52378  * 
52379  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
52380  * it needs 
52381  * xgrid.store = Roo.data.Store
52382  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
52383  * xgrid.store.reader = Roo.data.JsonReader 
52384  * 
52385  * 
52386  * @constructor
52387  * Creates a new GridField
52388  * @param {Object} config Configuration options
52389  */
52390 Roo.form.GridField = function(config){
52391     Roo.form.GridField.superclass.constructor.call(this, config);
52392      
52393 };
52394
52395 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
52396     /**
52397      * @cfg {Number} width  - used to restrict width of grid..
52398      */
52399     width : 100,
52400     /**
52401      * @cfg {Number} height - used to restrict height of grid..
52402      */
52403     height : 50,
52404      /**
52405      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
52406          * 
52407          *}
52408      */
52409     xgrid : false, 
52410     /**
52411      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52412      * {tag: "input", type: "checkbox", autocomplete: "off"})
52413      */
52414    // defaultAutoCreate : { tag: 'div' },
52415     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
52416     /**
52417      * @cfg {String} addTitle Text to include for adding a title.
52418      */
52419     addTitle : false,
52420     //
52421     onResize : function(){
52422         Roo.form.Field.superclass.onResize.apply(this, arguments);
52423     },
52424
52425     initEvents : function(){
52426         // Roo.form.Checkbox.superclass.initEvents.call(this);
52427         // has no events...
52428        
52429     },
52430
52431
52432     getResizeEl : function(){
52433         return this.wrap;
52434     },
52435
52436     getPositionEl : function(){
52437         return this.wrap;
52438     },
52439
52440     // private
52441     onRender : function(ct, position){
52442         
52443         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
52444         var style = this.style;
52445         delete this.style;
52446         
52447         Roo.form.GridField.superclass.onRender.call(this, ct, position);
52448         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
52449         this.viewEl = this.wrap.createChild({ tag: 'div' });
52450         if (style) {
52451             this.viewEl.applyStyles(style);
52452         }
52453         if (this.width) {
52454             this.viewEl.setWidth(this.width);
52455         }
52456         if (this.height) {
52457             this.viewEl.setHeight(this.height);
52458         }
52459         //if(this.inputValue !== undefined){
52460         //this.setValue(this.value);
52461         
52462         
52463         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
52464         
52465         
52466         this.grid.render();
52467         this.grid.getDataSource().on('remove', this.refreshValue, this);
52468         this.grid.getDataSource().on('update', this.refreshValue, this);
52469         this.grid.on('afteredit', this.refreshValue, this);
52470  
52471     },
52472      
52473     
52474     /**
52475      * Sets the value of the item. 
52476      * @param {String} either an object  or a string..
52477      */
52478     setValue : function(v){
52479         //this.value = v;
52480         v = v || []; // empty set..
52481         // this does not seem smart - it really only affects memoryproxy grids..
52482         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
52483             var ds = this.grid.getDataSource();
52484             // assumes a json reader..
52485             var data = {}
52486             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
52487             ds.loadData( data);
52488         }
52489         // clear selection so it does not get stale.
52490         if (this.grid.sm) { 
52491             this.grid.sm.clearSelections();
52492         }
52493         
52494         Roo.form.GridField.superclass.setValue.call(this, v);
52495         this.refreshValue();
52496         // should load data in the grid really....
52497     },
52498     
52499     // private
52500     refreshValue: function() {
52501          var val = [];
52502         this.grid.getDataSource().each(function(r) {
52503             val.push(r.data);
52504         });
52505         this.el.dom.value = Roo.encode(val);
52506     }
52507     
52508      
52509     
52510     
52511 });/*
52512  * Based on:
52513  * Ext JS Library 1.1.1
52514  * Copyright(c) 2006-2007, Ext JS, LLC.
52515  *
52516  * Originally Released Under LGPL - original licence link has changed is not relivant.
52517  *
52518  * Fork - LGPL
52519  * <script type="text/javascript">
52520  */
52521 /**
52522  * @class Roo.form.DisplayField
52523  * @extends Roo.form.Field
52524  * A generic Field to display non-editable data.
52525  * @cfg {Boolean} closable (true|false) default false
52526  * @constructor
52527  * Creates a new Display Field item.
52528  * @param {Object} config Configuration options
52529  */
52530 Roo.form.DisplayField = function(config){
52531     Roo.form.DisplayField.superclass.constructor.call(this, config);
52532     
52533     this.addEvents({
52534         /**
52535          * @event close
52536          * Fires after the click the close btn
52537              * @param {Roo.form.DisplayField} this
52538              */
52539         close : true
52540     });
52541 };
52542
52543 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
52544     inputType:      'hidden',
52545     allowBlank:     true,
52546     readOnly:         true,
52547     
52548  
52549     /**
52550      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52551      */
52552     focusClass : undefined,
52553     /**
52554      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52555      */
52556     fieldClass: 'x-form-field',
52557     
52558      /**
52559      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
52560      */
52561     valueRenderer: undefined,
52562     
52563     width: 100,
52564     /**
52565      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52566      * {tag: "input", type: "checkbox", autocomplete: "off"})
52567      */
52568      
52569  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
52570  
52571     closable : false,
52572     
52573     onResize : function(){
52574         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
52575         
52576     },
52577
52578     initEvents : function(){
52579         // Roo.form.Checkbox.superclass.initEvents.call(this);
52580         // has no events...
52581         
52582         if(this.closable){
52583             this.closeEl.on('click', this.onClose, this);
52584         }
52585        
52586     },
52587
52588
52589     getResizeEl : function(){
52590         return this.wrap;
52591     },
52592
52593     getPositionEl : function(){
52594         return this.wrap;
52595     },
52596
52597     // private
52598     onRender : function(ct, position){
52599         
52600         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
52601         //if(this.inputValue !== undefined){
52602         this.wrap = this.el.wrap();
52603         
52604         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
52605         
52606         if(this.closable){
52607             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
52608         }
52609         
52610         if (this.bodyStyle) {
52611             this.viewEl.applyStyles(this.bodyStyle);
52612         }
52613         //this.viewEl.setStyle('padding', '2px');
52614         
52615         this.setValue(this.value);
52616         
52617     },
52618 /*
52619     // private
52620     initValue : Roo.emptyFn,
52621
52622   */
52623
52624         // private
52625     onClick : function(){
52626         
52627     },
52628
52629     /**
52630      * Sets the checked state of the checkbox.
52631      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
52632      */
52633     setValue : function(v){
52634         this.value = v;
52635         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
52636         // this might be called before we have a dom element..
52637         if (!this.viewEl) {
52638             return;
52639         }
52640         this.viewEl.dom.innerHTML = html;
52641         Roo.form.DisplayField.superclass.setValue.call(this, v);
52642
52643     },
52644     
52645     onClose : function(e)
52646     {
52647         e.preventDefault();
52648         
52649         this.fireEvent('close', this);
52650     }
52651 });/*
52652  * 
52653  * Licence- LGPL
52654  * 
52655  */
52656
52657 /**
52658  * @class Roo.form.DayPicker
52659  * @extends Roo.form.Field
52660  * A Day picker show [M] [T] [W] ....
52661  * @constructor
52662  * Creates a new Day Picker
52663  * @param {Object} config Configuration options
52664  */
52665 Roo.form.DayPicker= function(config){
52666     Roo.form.DayPicker.superclass.constructor.call(this, config);
52667      
52668 };
52669
52670 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
52671     /**
52672      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52673      */
52674     focusClass : undefined,
52675     /**
52676      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52677      */
52678     fieldClass: "x-form-field",
52679    
52680     /**
52681      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52682      * {tag: "input", type: "checkbox", autocomplete: "off"})
52683      */
52684     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
52685     
52686    
52687     actionMode : 'viewEl', 
52688     //
52689     // private
52690  
52691     inputType : 'hidden',
52692     
52693      
52694     inputElement: false, // real input element?
52695     basedOn: false, // ????
52696     
52697     isFormField: true, // not sure where this is needed!!!!
52698
52699     onResize : function(){
52700         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
52701         if(!this.boxLabel){
52702             this.el.alignTo(this.wrap, 'c-c');
52703         }
52704     },
52705
52706     initEvents : function(){
52707         Roo.form.Checkbox.superclass.initEvents.call(this);
52708         this.el.on("click", this.onClick,  this);
52709         this.el.on("change", this.onClick,  this);
52710     },
52711
52712
52713     getResizeEl : function(){
52714         return this.wrap;
52715     },
52716
52717     getPositionEl : function(){
52718         return this.wrap;
52719     },
52720
52721     
52722     // private
52723     onRender : function(ct, position){
52724         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
52725        
52726         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
52727         
52728         var r1 = '<table><tr>';
52729         var r2 = '<tr class="x-form-daypick-icons">';
52730         for (var i=0; i < 7; i++) {
52731             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
52732             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
52733         }
52734         
52735         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
52736         viewEl.select('img').on('click', this.onClick, this);
52737         this.viewEl = viewEl;   
52738         
52739         
52740         // this will not work on Chrome!!!
52741         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
52742         this.el.on('propertychange', this.setFromHidden,  this);  //ie
52743         
52744         
52745           
52746
52747     },
52748
52749     // private
52750     initValue : Roo.emptyFn,
52751
52752     /**
52753      * Returns the checked state of the checkbox.
52754      * @return {Boolean} True if checked, else false
52755      */
52756     getValue : function(){
52757         return this.el.dom.value;
52758         
52759     },
52760
52761         // private
52762     onClick : function(e){ 
52763         //this.setChecked(!this.checked);
52764         Roo.get(e.target).toggleClass('x-menu-item-checked');
52765         this.refreshValue();
52766         //if(this.el.dom.checked != this.checked){
52767         //    this.setValue(this.el.dom.checked);
52768        // }
52769     },
52770     
52771     // private
52772     refreshValue : function()
52773     {
52774         var val = '';
52775         this.viewEl.select('img',true).each(function(e,i,n)  {
52776             val += e.is(".x-menu-item-checked") ? String(n) : '';
52777         });
52778         this.setValue(val, true);
52779     },
52780
52781     /**
52782      * Sets the checked state of the checkbox.
52783      * On is always based on a string comparison between inputValue and the param.
52784      * @param {Boolean/String} value - the value to set 
52785      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
52786      */
52787     setValue : function(v,suppressEvent){
52788         if (!this.el.dom) {
52789             return;
52790         }
52791         var old = this.el.dom.value ;
52792         this.el.dom.value = v;
52793         if (suppressEvent) {
52794             return ;
52795         }
52796          
52797         // update display..
52798         this.viewEl.select('img',true).each(function(e,i,n)  {
52799             
52800             var on = e.is(".x-menu-item-checked");
52801             var newv = v.indexOf(String(n)) > -1;
52802             if (on != newv) {
52803                 e.toggleClass('x-menu-item-checked');
52804             }
52805             
52806         });
52807         
52808         
52809         this.fireEvent('change', this, v, old);
52810         
52811         
52812     },
52813    
52814     // handle setting of hidden value by some other method!!?!?
52815     setFromHidden: function()
52816     {
52817         if(!this.el){
52818             return;
52819         }
52820         //console.log("SET FROM HIDDEN");
52821         //alert('setFrom hidden');
52822         this.setValue(this.el.dom.value);
52823     },
52824     
52825     onDestroy : function()
52826     {
52827         if(this.viewEl){
52828             Roo.get(this.viewEl).remove();
52829         }
52830          
52831         Roo.form.DayPicker.superclass.onDestroy.call(this);
52832     }
52833
52834 });/*
52835  * RooJS Library 1.1.1
52836  * Copyright(c) 2008-2011  Alan Knowles
52837  *
52838  * License - LGPL
52839  */
52840  
52841
52842 /**
52843  * @class Roo.form.ComboCheck
52844  * @extends Roo.form.ComboBox
52845  * A combobox for multiple select items.
52846  *
52847  * FIXME - could do with a reset button..
52848  * 
52849  * @constructor
52850  * Create a new ComboCheck
52851  * @param {Object} config Configuration options
52852  */
52853 Roo.form.ComboCheck = function(config){
52854     Roo.form.ComboCheck.superclass.constructor.call(this, config);
52855     // should verify some data...
52856     // like
52857     // hiddenName = required..
52858     // displayField = required
52859     // valudField == required
52860     var req= [ 'hiddenName', 'displayField', 'valueField' ];
52861     var _t = this;
52862     Roo.each(req, function(e) {
52863         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
52864             throw "Roo.form.ComboCheck : missing value for: " + e;
52865         }
52866     });
52867     
52868     
52869 };
52870
52871 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
52872      
52873      
52874     editable : false,
52875      
52876     selectedClass: 'x-menu-item-checked', 
52877     
52878     // private
52879     onRender : function(ct, position){
52880         var _t = this;
52881         
52882         
52883         
52884         if(!this.tpl){
52885             var cls = 'x-combo-list';
52886
52887             
52888             this.tpl =  new Roo.Template({
52889                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
52890                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
52891                    '<span>{' + this.displayField + '}</span>' +
52892                     '</div>' 
52893                 
52894             });
52895         }
52896  
52897         
52898         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
52899         this.view.singleSelect = false;
52900         this.view.multiSelect = true;
52901         this.view.toggleSelect = true;
52902         this.pageTb.add(new Roo.Toolbar.Fill(), {
52903             
52904             text: 'Done',
52905             handler: function()
52906             {
52907                 _t.collapse();
52908             }
52909         });
52910     },
52911     
52912     onViewOver : function(e, t){
52913         // do nothing...
52914         return;
52915         
52916     },
52917     
52918     onViewClick : function(doFocus,index){
52919         return;
52920         
52921     },
52922     select: function () {
52923         //Roo.log("SELECT CALLED");
52924     },
52925      
52926     selectByValue : function(xv, scrollIntoView){
52927         var ar = this.getValueArray();
52928         var sels = [];
52929         
52930         Roo.each(ar, function(v) {
52931             if(v === undefined || v === null){
52932                 return;
52933             }
52934             var r = this.findRecord(this.valueField, v);
52935             if(r){
52936                 sels.push(this.store.indexOf(r))
52937                 
52938             }
52939         },this);
52940         this.view.select(sels);
52941         return false;
52942     },
52943     
52944     
52945     
52946     onSelect : function(record, index){
52947        // Roo.log("onselect Called");
52948        // this is only called by the clear button now..
52949         this.view.clearSelections();
52950         this.setValue('[]');
52951         if (this.value != this.valueBefore) {
52952             this.fireEvent('change', this, this.value, this.valueBefore);
52953             this.valueBefore = this.value;
52954         }
52955     },
52956     getValueArray : function()
52957     {
52958         var ar = [] ;
52959         
52960         try {
52961             //Roo.log(this.value);
52962             if (typeof(this.value) == 'undefined') {
52963                 return [];
52964             }
52965             var ar = Roo.decode(this.value);
52966             return  ar instanceof Array ? ar : []; //?? valid?
52967             
52968         } catch(e) {
52969             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
52970             return [];
52971         }
52972          
52973     },
52974     expand : function ()
52975     {
52976         
52977         Roo.form.ComboCheck.superclass.expand.call(this);
52978         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
52979         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
52980         
52981
52982     },
52983     
52984     collapse : function(){
52985         Roo.form.ComboCheck.superclass.collapse.call(this);
52986         var sl = this.view.getSelectedIndexes();
52987         var st = this.store;
52988         var nv = [];
52989         var tv = [];
52990         var r;
52991         Roo.each(sl, function(i) {
52992             r = st.getAt(i);
52993             nv.push(r.get(this.valueField));
52994         },this);
52995         this.setValue(Roo.encode(nv));
52996         if (this.value != this.valueBefore) {
52997
52998             this.fireEvent('change', this, this.value, this.valueBefore);
52999             this.valueBefore = this.value;
53000         }
53001         
53002     },
53003     
53004     setValue : function(v){
53005         // Roo.log(v);
53006         this.value = v;
53007         
53008         var vals = this.getValueArray();
53009         var tv = [];
53010         Roo.each(vals, function(k) {
53011             var r = this.findRecord(this.valueField, k);
53012             if(r){
53013                 tv.push(r.data[this.displayField]);
53014             }else if(this.valueNotFoundText !== undefined){
53015                 tv.push( this.valueNotFoundText );
53016             }
53017         },this);
53018        // Roo.log(tv);
53019         
53020         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
53021         this.hiddenField.value = v;
53022         this.value = v;
53023     }
53024     
53025 });/*
53026  * Based on:
53027  * Ext JS Library 1.1.1
53028  * Copyright(c) 2006-2007, Ext JS, LLC.
53029  *
53030  * Originally Released Under LGPL - original licence link has changed is not relivant.
53031  *
53032  * Fork - LGPL
53033  * <script type="text/javascript">
53034  */
53035  
53036 /**
53037  * @class Roo.form.Signature
53038  * @extends Roo.form.Field
53039  * Signature field.  
53040  * @constructor
53041  * 
53042  * @param {Object} config Configuration options
53043  */
53044
53045 Roo.form.Signature = function(config){
53046     Roo.form.Signature.superclass.constructor.call(this, config);
53047     
53048     this.addEvents({// not in used??
53049          /**
53050          * @event confirm
53051          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
53052              * @param {Roo.form.Signature} combo This combo box
53053              */
53054         'confirm' : true,
53055         /**
53056          * @event reset
53057          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
53058              * @param {Roo.form.ComboBox} combo This combo box
53059              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
53060              */
53061         'reset' : true
53062     });
53063 };
53064
53065 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
53066     /**
53067      * @cfg {Object} labels Label to use when rendering a form.
53068      * defaults to 
53069      * labels : { 
53070      *      clear : "Clear",
53071      *      confirm : "Confirm"
53072      *  }
53073      */
53074     labels : { 
53075         clear : "Clear",
53076         confirm : "Confirm"
53077     },
53078     /**
53079      * @cfg {Number} width The signature panel width (defaults to 300)
53080      */
53081     width: 300,
53082     /**
53083      * @cfg {Number} height The signature panel height (defaults to 100)
53084      */
53085     height : 100,
53086     /**
53087      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
53088      */
53089     allowBlank : false,
53090     
53091     //private
53092     // {Object} signPanel The signature SVG panel element (defaults to {})
53093     signPanel : {},
53094     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
53095     isMouseDown : false,
53096     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
53097     isConfirmed : false,
53098     // {String} signatureTmp SVG mapping string (defaults to empty string)
53099     signatureTmp : '',
53100     
53101     
53102     defaultAutoCreate : { // modified by initCompnoent..
53103         tag: "input",
53104         type:"hidden"
53105     },
53106
53107     // private
53108     onRender : function(ct, position){
53109         
53110         Roo.form.Signature.superclass.onRender.call(this, ct, position);
53111         
53112         this.wrap = this.el.wrap({
53113             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
53114         });
53115         
53116         this.createToolbar(this);
53117         this.signPanel = this.wrap.createChild({
53118                 tag: 'div',
53119                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
53120             }, this.el
53121         );
53122             
53123         this.svgID = Roo.id();
53124         this.svgEl = this.signPanel.createChild({
53125               xmlns : 'http://www.w3.org/2000/svg',
53126               tag : 'svg',
53127               id : this.svgID + "-svg",
53128               width: this.width,
53129               height: this.height,
53130               viewBox: '0 0 '+this.width+' '+this.height,
53131               cn : [
53132                 {
53133                     tag: "rect",
53134                     id: this.svgID + "-svg-r",
53135                     width: this.width,
53136                     height: this.height,
53137                     fill: "#ffa"
53138                 },
53139                 {
53140                     tag: "line",
53141                     id: this.svgID + "-svg-l",
53142                     x1: "0", // start
53143                     y1: (this.height*0.8), // start set the line in 80% of height
53144                     x2: this.width, // end
53145                     y2: (this.height*0.8), // end set the line in 80% of height
53146                     'stroke': "#666",
53147                     'stroke-width': "1",
53148                     'stroke-dasharray': "3",
53149                     'shape-rendering': "crispEdges",
53150                     'pointer-events': "none"
53151                 },
53152                 {
53153                     tag: "path",
53154                     id: this.svgID + "-svg-p",
53155                     'stroke': "navy",
53156                     'stroke-width': "3",
53157                     'fill': "none",
53158                     'pointer-events': 'none'
53159                 }
53160               ]
53161         });
53162         this.createSVG();
53163         this.svgBox = this.svgEl.dom.getScreenCTM();
53164     },
53165     createSVG : function(){ 
53166         var svg = this.signPanel;
53167         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
53168         var t = this;
53169
53170         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
53171         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
53172         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
53173         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
53174         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
53175         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
53176         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
53177         
53178     },
53179     isTouchEvent : function(e){
53180         return e.type.match(/^touch/);
53181     },
53182     getCoords : function (e) {
53183         var pt    = this.svgEl.dom.createSVGPoint();
53184         pt.x = e.clientX; 
53185         pt.y = e.clientY;
53186         if (this.isTouchEvent(e)) {
53187             pt.x =  e.targetTouches[0].clientX;
53188             pt.y = e.targetTouches[0].clientY;
53189         }
53190         var a = this.svgEl.dom.getScreenCTM();
53191         var b = a.inverse();
53192         var mx = pt.matrixTransform(b);
53193         return mx.x + ',' + mx.y;
53194     },
53195     //mouse event headler 
53196     down : function (e) {
53197         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
53198         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
53199         
53200         this.isMouseDown = true;
53201         
53202         e.preventDefault();
53203     },
53204     move : function (e) {
53205         if (this.isMouseDown) {
53206             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
53207             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
53208         }
53209         
53210         e.preventDefault();
53211     },
53212     up : function (e) {
53213         this.isMouseDown = false;
53214         var sp = this.signatureTmp.split(' ');
53215         
53216         if(sp.length > 1){
53217             if(!sp[sp.length-2].match(/^L/)){
53218                 sp.pop();
53219                 sp.pop();
53220                 sp.push("");
53221                 this.signatureTmp = sp.join(" ");
53222             }
53223         }
53224         if(this.getValue() != this.signatureTmp){
53225             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53226             this.isConfirmed = false;
53227         }
53228         e.preventDefault();
53229     },
53230     
53231     /**
53232      * Protected method that will not generally be called directly. It
53233      * is called when the editor creates its toolbar. Override this method if you need to
53234      * add custom toolbar buttons.
53235      * @param {HtmlEditor} editor
53236      */
53237     createToolbar : function(editor){
53238          function btn(id, toggle, handler){
53239             var xid = fid + '-'+ id ;
53240             return {
53241                 id : xid,
53242                 cmd : id,
53243                 cls : 'x-btn-icon x-edit-'+id,
53244                 enableToggle:toggle !== false,
53245                 scope: editor, // was editor...
53246                 handler:handler||editor.relayBtnCmd,
53247                 clickEvent:'mousedown',
53248                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53249                 tabIndex:-1
53250             };
53251         }
53252         
53253         
53254         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
53255         this.tb = tb;
53256         this.tb.add(
53257            {
53258                 cls : ' x-signature-btn x-signature-'+id,
53259                 scope: editor, // was editor...
53260                 handler: this.reset,
53261                 clickEvent:'mousedown',
53262                 text: this.labels.clear
53263             },
53264             {
53265                  xtype : 'Fill',
53266                  xns: Roo.Toolbar
53267             }, 
53268             {
53269                 cls : '  x-signature-btn x-signature-'+id,
53270                 scope: editor, // was editor...
53271                 handler: this.confirmHandler,
53272                 clickEvent:'mousedown',
53273                 text: this.labels.confirm
53274             }
53275         );
53276     
53277     },
53278     //public
53279     /**
53280      * when user is clicked confirm then show this image.....
53281      * 
53282      * @return {String} Image Data URI
53283      */
53284     getImageDataURI : function(){
53285         var svg = this.svgEl.dom.parentNode.innerHTML;
53286         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
53287         return src; 
53288     },
53289     /**
53290      * 
53291      * @return {Boolean} this.isConfirmed
53292      */
53293     getConfirmed : function(){
53294         return this.isConfirmed;
53295     },
53296     /**
53297      * 
53298      * @return {Number} this.width
53299      */
53300     getWidth : function(){
53301         return this.width;
53302     },
53303     /**
53304      * 
53305      * @return {Number} this.height
53306      */
53307     getHeight : function(){
53308         return this.height;
53309     },
53310     // private
53311     getSignature : function(){
53312         return this.signatureTmp;
53313     },
53314     // private
53315     reset : function(){
53316         this.signatureTmp = '';
53317         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53318         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
53319         this.isConfirmed = false;
53320         Roo.form.Signature.superclass.reset.call(this);
53321     },
53322     setSignature : function(s){
53323         this.signatureTmp = s;
53324         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53325         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
53326         this.setValue(s);
53327         this.isConfirmed = false;
53328         Roo.form.Signature.superclass.reset.call(this);
53329     }, 
53330     test : function(){
53331 //        Roo.log(this.signPanel.dom.contentWindow.up())
53332     },
53333     //private
53334     setConfirmed : function(){
53335         
53336         
53337         
53338 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
53339     },
53340     // private
53341     confirmHandler : function(){
53342         if(!this.getSignature()){
53343             return;
53344         }
53345         
53346         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
53347         this.setValue(this.getSignature());
53348         this.isConfirmed = true;
53349         
53350         this.fireEvent('confirm', this);
53351     },
53352     // private
53353     // Subclasses should provide the validation implementation by overriding this
53354     validateValue : function(value){
53355         if(this.allowBlank){
53356             return true;
53357         }
53358         
53359         if(this.isConfirmed){
53360             return true;
53361         }
53362         return false;
53363     }
53364 });/*
53365  * Based on:
53366  * Ext JS Library 1.1.1
53367  * Copyright(c) 2006-2007, Ext JS, LLC.
53368  *
53369  * Originally Released Under LGPL - original licence link has changed is not relivant.
53370  *
53371  * Fork - LGPL
53372  * <script type="text/javascript">
53373  */
53374  
53375
53376 /**
53377  * @class Roo.form.ComboBox
53378  * @extends Roo.form.TriggerField
53379  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
53380  * @constructor
53381  * Create a new ComboBox.
53382  * @param {Object} config Configuration options
53383  */
53384 Roo.form.Select = function(config){
53385     Roo.form.Select.superclass.constructor.call(this, config);
53386      
53387 };
53388
53389 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
53390     /**
53391      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
53392      */
53393     /**
53394      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
53395      * rendering into an Roo.Editor, defaults to false)
53396      */
53397     /**
53398      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
53399      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
53400      */
53401     /**
53402      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
53403      */
53404     /**
53405      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
53406      * the dropdown list (defaults to undefined, with no header element)
53407      */
53408
53409      /**
53410      * @cfg {String/Roo.Template} tpl The template to use to render the output
53411      */
53412      
53413     // private
53414     defaultAutoCreate : {tag: "select"  },
53415     /**
53416      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
53417      */
53418     listWidth: undefined,
53419     /**
53420      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
53421      * mode = 'remote' or 'text' if mode = 'local')
53422      */
53423     displayField: undefined,
53424     /**
53425      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
53426      * mode = 'remote' or 'value' if mode = 'local'). 
53427      * Note: use of a valueField requires the user make a selection
53428      * in order for a value to be mapped.
53429      */
53430     valueField: undefined,
53431     
53432     
53433     /**
53434      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
53435      * field's data value (defaults to the underlying DOM element's name)
53436      */
53437     hiddenName: undefined,
53438     /**
53439      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
53440      */
53441     listClass: '',
53442     /**
53443      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
53444      */
53445     selectedClass: 'x-combo-selected',
53446     /**
53447      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
53448      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
53449      * which displays a downward arrow icon).
53450      */
53451     triggerClass : 'x-form-arrow-trigger',
53452     /**
53453      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
53454      */
53455     shadow:'sides',
53456     /**
53457      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
53458      * anchor positions (defaults to 'tl-bl')
53459      */
53460     listAlign: 'tl-bl?',
53461     /**
53462      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
53463      */
53464     maxHeight: 300,
53465     /**
53466      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
53467      * query specified by the allQuery config option (defaults to 'query')
53468      */
53469     triggerAction: 'query',
53470     /**
53471      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
53472      * (defaults to 4, does not apply if editable = false)
53473      */
53474     minChars : 4,
53475     /**
53476      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
53477      * delay (typeAheadDelay) if it matches a known value (defaults to false)
53478      */
53479     typeAhead: false,
53480     /**
53481      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
53482      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
53483      */
53484     queryDelay: 500,
53485     /**
53486      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
53487      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
53488      */
53489     pageSize: 0,
53490     /**
53491      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
53492      * when editable = true (defaults to false)
53493      */
53494     selectOnFocus:false,
53495     /**
53496      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
53497      */
53498     queryParam: 'query',
53499     /**
53500      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
53501      * when mode = 'remote' (defaults to 'Loading...')
53502      */
53503     loadingText: 'Loading...',
53504     /**
53505      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
53506      */
53507     resizable: false,
53508     /**
53509      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
53510      */
53511     handleHeight : 8,
53512     /**
53513      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
53514      * traditional select (defaults to true)
53515      */
53516     editable: true,
53517     /**
53518      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
53519      */
53520     allQuery: '',
53521     /**
53522      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
53523      */
53524     mode: 'remote',
53525     /**
53526      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
53527      * listWidth has a higher value)
53528      */
53529     minListWidth : 70,
53530     /**
53531      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
53532      * allow the user to set arbitrary text into the field (defaults to false)
53533      */
53534     forceSelection:false,
53535     /**
53536      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
53537      * if typeAhead = true (defaults to 250)
53538      */
53539     typeAheadDelay : 250,
53540     /**
53541      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
53542      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
53543      */
53544     valueNotFoundText : undefined,
53545     
53546     /**
53547      * @cfg {String} defaultValue The value displayed after loading the store.
53548      */
53549     defaultValue: '',
53550     
53551     /**
53552      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
53553      */
53554     blockFocus : false,
53555     
53556     /**
53557      * @cfg {Boolean} disableClear Disable showing of clear button.
53558      */
53559     disableClear : false,
53560     /**
53561      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
53562      */
53563     alwaysQuery : false,
53564     
53565     //private
53566     addicon : false,
53567     editicon: false,
53568     
53569     // element that contains real text value.. (when hidden is used..)
53570      
53571     // private
53572     onRender : function(ct, position){
53573         Roo.form.Field.prototype.onRender.call(this, ct, position);
53574         
53575         if(this.store){
53576             this.store.on('beforeload', this.onBeforeLoad, this);
53577             this.store.on('load', this.onLoad, this);
53578             this.store.on('loadexception', this.onLoadException, this);
53579             this.store.load({});
53580         }
53581         
53582         
53583         
53584     },
53585
53586     // private
53587     initEvents : function(){
53588         //Roo.form.ComboBox.superclass.initEvents.call(this);
53589  
53590     },
53591
53592     onDestroy : function(){
53593        
53594         if(this.store){
53595             this.store.un('beforeload', this.onBeforeLoad, this);
53596             this.store.un('load', this.onLoad, this);
53597             this.store.un('loadexception', this.onLoadException, this);
53598         }
53599         //Roo.form.ComboBox.superclass.onDestroy.call(this);
53600     },
53601
53602     // private
53603     fireKey : function(e){
53604         if(e.isNavKeyPress() && !this.list.isVisible()){
53605             this.fireEvent("specialkey", this, e);
53606         }
53607     },
53608
53609     // private
53610     onResize: function(w, h){
53611         
53612         return; 
53613     
53614         
53615     },
53616
53617     /**
53618      * Allow or prevent the user from directly editing the field text.  If false is passed,
53619      * the user will only be able to select from the items defined in the dropdown list.  This method
53620      * is the runtime equivalent of setting the 'editable' config option at config time.
53621      * @param {Boolean} value True to allow the user to directly edit the field text
53622      */
53623     setEditable : function(value){
53624          
53625     },
53626
53627     // private
53628     onBeforeLoad : function(){
53629         
53630         Roo.log("Select before load");
53631         return;
53632     
53633         this.innerList.update(this.loadingText ?
53634                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
53635         //this.restrictHeight();
53636         this.selectedIndex = -1;
53637     },
53638
53639     // private
53640     onLoad : function(){
53641
53642     
53643         var dom = this.el.dom;
53644         dom.innerHTML = '';
53645          var od = dom.ownerDocument;
53646          
53647         if (this.emptyText) {
53648             var op = od.createElement('option');
53649             op.setAttribute('value', '');
53650             op.innerHTML = String.format('{0}', this.emptyText);
53651             dom.appendChild(op);
53652         }
53653         if(this.store.getCount() > 0){
53654            
53655             var vf = this.valueField;
53656             var df = this.displayField;
53657             this.store.data.each(function(r) {
53658                 // which colmsn to use... testing - cdoe / title..
53659                 var op = od.createElement('option');
53660                 op.setAttribute('value', r.data[vf]);
53661                 op.innerHTML = String.format('{0}', r.data[df]);
53662                 dom.appendChild(op);
53663             });
53664             if (typeof(this.defaultValue != 'undefined')) {
53665                 this.setValue(this.defaultValue);
53666             }
53667             
53668              
53669         }else{
53670             //this.onEmptyResults();
53671         }
53672         //this.el.focus();
53673     },
53674     // private
53675     onLoadException : function()
53676     {
53677         dom.innerHTML = '';
53678             
53679         Roo.log("Select on load exception");
53680         return;
53681     
53682         this.collapse();
53683         Roo.log(this.store.reader.jsonData);
53684         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
53685             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
53686         }
53687         
53688         
53689     },
53690     // private
53691     onTypeAhead : function(){
53692          
53693     },
53694
53695     // private
53696     onSelect : function(record, index){
53697         Roo.log('on select?');
53698         return;
53699         if(this.fireEvent('beforeselect', this, record, index) !== false){
53700             this.setFromData(index > -1 ? record.data : false);
53701             this.collapse();
53702             this.fireEvent('select', this, record, index);
53703         }
53704     },
53705
53706     /**
53707      * Returns the currently selected field value or empty string if no value is set.
53708      * @return {String} value The selected value
53709      */
53710     getValue : function(){
53711         var dom = this.el.dom;
53712         this.value = dom.options[dom.selectedIndex].value;
53713         return this.value;
53714         
53715     },
53716
53717     /**
53718      * Clears any text/value currently set in the field
53719      */
53720     clearValue : function(){
53721         this.value = '';
53722         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
53723         
53724     },
53725
53726     /**
53727      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
53728      * will be displayed in the field.  If the value does not match the data value of an existing item,
53729      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
53730      * Otherwise the field will be blank (although the value will still be set).
53731      * @param {String} value The value to match
53732      */
53733     setValue : function(v){
53734         var d = this.el.dom;
53735         for (var i =0; i < d.options.length;i++) {
53736             if (v == d.options[i].value) {
53737                 d.selectedIndex = i;
53738                 this.value = v;
53739                 return;
53740             }
53741         }
53742         this.clearValue();
53743     },
53744     /**
53745      * @property {Object} the last set data for the element
53746      */
53747     
53748     lastData : false,
53749     /**
53750      * Sets the value of the field based on a object which is related to the record format for the store.
53751      * @param {Object} value the value to set as. or false on reset?
53752      */
53753     setFromData : function(o){
53754         Roo.log('setfrom data?');
53755          
53756         
53757         
53758     },
53759     // private
53760     reset : function(){
53761         this.clearValue();
53762     },
53763     // private
53764     findRecord : function(prop, value){
53765         
53766         return false;
53767     
53768         var record;
53769         if(this.store.getCount() > 0){
53770             this.store.each(function(r){
53771                 if(r.data[prop] == value){
53772                     record = r;
53773                     return false;
53774                 }
53775                 return true;
53776             });
53777         }
53778         return record;
53779     },
53780     
53781     getName: function()
53782     {
53783         // returns hidden if it's set..
53784         if (!this.rendered) {return ''};
53785         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
53786         
53787     },
53788      
53789
53790     
53791
53792     // private
53793     onEmptyResults : function(){
53794         Roo.log('empty results');
53795         //this.collapse();
53796     },
53797
53798     /**
53799      * Returns true if the dropdown list is expanded, else false.
53800      */
53801     isExpanded : function(){
53802         return false;
53803     },
53804
53805     /**
53806      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
53807      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
53808      * @param {String} value The data value of the item to select
53809      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
53810      * selected item if it is not currently in view (defaults to true)
53811      * @return {Boolean} True if the value matched an item in the list, else false
53812      */
53813     selectByValue : function(v, scrollIntoView){
53814         Roo.log('select By Value');
53815         return false;
53816     
53817         if(v !== undefined && v !== null){
53818             var r = this.findRecord(this.valueField || this.displayField, v);
53819             if(r){
53820                 this.select(this.store.indexOf(r), scrollIntoView);
53821                 return true;
53822             }
53823         }
53824         return false;
53825     },
53826
53827     /**
53828      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
53829      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
53830      * @param {Number} index The zero-based index of the list item to select
53831      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
53832      * selected item if it is not currently in view (defaults to true)
53833      */
53834     select : function(index, scrollIntoView){
53835         Roo.log('select ');
53836         return  ;
53837         
53838         this.selectedIndex = index;
53839         this.view.select(index);
53840         if(scrollIntoView !== false){
53841             var el = this.view.getNode(index);
53842             if(el){
53843                 this.innerList.scrollChildIntoView(el, false);
53844             }
53845         }
53846     },
53847
53848       
53849
53850     // private
53851     validateBlur : function(){
53852         
53853         return;
53854         
53855     },
53856
53857     // private
53858     initQuery : function(){
53859         this.doQuery(this.getRawValue());
53860     },
53861
53862     // private
53863     doForce : function(){
53864         if(this.el.dom.value.length > 0){
53865             this.el.dom.value =
53866                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
53867              
53868         }
53869     },
53870
53871     /**
53872      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
53873      * query allowing the query action to be canceled if needed.
53874      * @param {String} query The SQL query to execute
53875      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
53876      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
53877      * saved in the current store (defaults to false)
53878      */
53879     doQuery : function(q, forceAll){
53880         
53881         Roo.log('doQuery?');
53882         if(q === undefined || q === null){
53883             q = '';
53884         }
53885         var qe = {
53886             query: q,
53887             forceAll: forceAll,
53888             combo: this,
53889             cancel:false
53890         };
53891         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
53892             return false;
53893         }
53894         q = qe.query;
53895         forceAll = qe.forceAll;
53896         if(forceAll === true || (q.length >= this.minChars)){
53897             if(this.lastQuery != q || this.alwaysQuery){
53898                 this.lastQuery = q;
53899                 if(this.mode == 'local'){
53900                     this.selectedIndex = -1;
53901                     if(forceAll){
53902                         this.store.clearFilter();
53903                     }else{
53904                         this.store.filter(this.displayField, q);
53905                     }
53906                     this.onLoad();
53907                 }else{
53908                     this.store.baseParams[this.queryParam] = q;
53909                     this.store.load({
53910                         params: this.getParams(q)
53911                     });
53912                     this.expand();
53913                 }
53914             }else{
53915                 this.selectedIndex = -1;
53916                 this.onLoad();   
53917             }
53918         }
53919     },
53920
53921     // private
53922     getParams : function(q){
53923         var p = {};
53924         //p[this.queryParam] = q;
53925         if(this.pageSize){
53926             p.start = 0;
53927             p.limit = this.pageSize;
53928         }
53929         return p;
53930     },
53931
53932     /**
53933      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
53934      */
53935     collapse : function(){
53936         
53937     },
53938
53939     // private
53940     collapseIf : function(e){
53941         
53942     },
53943
53944     /**
53945      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
53946      */
53947     expand : function(){
53948         
53949     } ,
53950
53951     // private
53952      
53953
53954     /** 
53955     * @cfg {Boolean} grow 
53956     * @hide 
53957     */
53958     /** 
53959     * @cfg {Number} growMin 
53960     * @hide 
53961     */
53962     /** 
53963     * @cfg {Number} growMax 
53964     * @hide 
53965     */
53966     /**
53967      * @hide
53968      * @method autoSize
53969      */
53970     
53971     setWidth : function()
53972     {
53973         
53974     },
53975     getResizeEl : function(){
53976         return this.el;
53977     }
53978 });//<script type="text/javasscript">
53979  
53980
53981 /**
53982  * @class Roo.DDView
53983  * A DnD enabled version of Roo.View.
53984  * @param {Element/String} container The Element in which to create the View.
53985  * @param {String} tpl The template string used to create the markup for each element of the View
53986  * @param {Object} config The configuration properties. These include all the config options of
53987  * {@link Roo.View} plus some specific to this class.<br>
53988  * <p>
53989  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
53990  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
53991  * <p>
53992  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
53993 .x-view-drag-insert-above {
53994         border-top:1px dotted #3366cc;
53995 }
53996 .x-view-drag-insert-below {
53997         border-bottom:1px dotted #3366cc;
53998 }
53999 </code></pre>
54000  * 
54001  */
54002  
54003 Roo.DDView = function(container, tpl, config) {
54004     Roo.DDView.superclass.constructor.apply(this, arguments);
54005     this.getEl().setStyle("outline", "0px none");
54006     this.getEl().unselectable();
54007     if (this.dragGroup) {
54008         this.setDraggable(this.dragGroup.split(","));
54009     }
54010     if (this.dropGroup) {
54011         this.setDroppable(this.dropGroup.split(","));
54012     }
54013     if (this.deletable) {
54014         this.setDeletable();
54015     }
54016     this.isDirtyFlag = false;
54017         this.addEvents({
54018                 "drop" : true
54019         });
54020 };
54021
54022 Roo.extend(Roo.DDView, Roo.View, {
54023 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
54024 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
54025 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
54026 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
54027
54028         isFormField: true,
54029
54030         reset: Roo.emptyFn,
54031         
54032         clearInvalid: Roo.form.Field.prototype.clearInvalid,
54033
54034         validate: function() {
54035                 return true;
54036         },
54037         
54038         destroy: function() {
54039                 this.purgeListeners();
54040                 this.getEl.removeAllListeners();
54041                 this.getEl().remove();
54042                 if (this.dragZone) {
54043                         if (this.dragZone.destroy) {
54044                                 this.dragZone.destroy();
54045                         }
54046                 }
54047                 if (this.dropZone) {
54048                         if (this.dropZone.destroy) {
54049                                 this.dropZone.destroy();
54050                         }
54051                 }
54052         },
54053
54054 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
54055         getName: function() {
54056                 return this.name;
54057         },
54058
54059 /**     Loads the View from a JSON string representing the Records to put into the Store. */
54060         setValue: function(v) {
54061                 if (!this.store) {
54062                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
54063                 }
54064                 var data = {};
54065                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
54066                 this.store.proxy = new Roo.data.MemoryProxy(data);
54067                 this.store.load();
54068         },
54069
54070 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
54071         getValue: function() {
54072                 var result = '(';
54073                 this.store.each(function(rec) {
54074                         result += rec.id + ',';
54075                 });
54076                 return result.substr(0, result.length - 1) + ')';
54077         },
54078         
54079         getIds: function() {
54080                 var i = 0, result = new Array(this.store.getCount());
54081                 this.store.each(function(rec) {
54082                         result[i++] = rec.id;
54083                 });
54084                 return result;
54085         },
54086         
54087         isDirty: function() {
54088                 return this.isDirtyFlag;
54089         },
54090
54091 /**
54092  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
54093  *      whole Element becomes the target, and this causes the drop gesture to append.
54094  */
54095     getTargetFromEvent : function(e) {
54096                 var target = e.getTarget();
54097                 while ((target !== null) && (target.parentNode != this.el.dom)) {
54098                 target = target.parentNode;
54099                 }
54100                 if (!target) {
54101                         target = this.el.dom.lastChild || this.el.dom;
54102                 }
54103                 return target;
54104     },
54105
54106 /**
54107  *      Create the drag data which consists of an object which has the property "ddel" as
54108  *      the drag proxy element. 
54109  */
54110     getDragData : function(e) {
54111         var target = this.findItemFromChild(e.getTarget());
54112                 if(target) {
54113                         this.handleSelection(e);
54114                         var selNodes = this.getSelectedNodes();
54115             var dragData = {
54116                 source: this,
54117                 copy: this.copy || (this.allowCopy && e.ctrlKey),
54118                 nodes: selNodes,
54119                 records: []
54120                         };
54121                         var selectedIndices = this.getSelectedIndexes();
54122                         for (var i = 0; i < selectedIndices.length; i++) {
54123                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
54124                         }
54125                         if (selNodes.length == 1) {
54126                                 dragData.ddel = target.cloneNode(true); // the div element
54127                         } else {
54128                                 var div = document.createElement('div'); // create the multi element drag "ghost"
54129                                 div.className = 'multi-proxy';
54130                                 for (var i = 0, len = selNodes.length; i < len; i++) {
54131                                         div.appendChild(selNodes[i].cloneNode(true));
54132                                 }
54133                                 dragData.ddel = div;
54134                         }
54135             //console.log(dragData)
54136             //console.log(dragData.ddel.innerHTML)
54137                         return dragData;
54138                 }
54139         //console.log('nodragData')
54140                 return false;
54141     },
54142     
54143 /**     Specify to which ddGroup items in this DDView may be dragged. */
54144     setDraggable: function(ddGroup) {
54145         if (ddGroup instanceof Array) {
54146                 Roo.each(ddGroup, this.setDraggable, this);
54147                 return;
54148         }
54149         if (this.dragZone) {
54150                 this.dragZone.addToGroup(ddGroup);
54151         } else {
54152                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
54153                                 containerScroll: true,
54154                                 ddGroup: ddGroup 
54155
54156                         });
54157 //                      Draggability implies selection. DragZone's mousedown selects the element.
54158                         if (!this.multiSelect) { this.singleSelect = true; }
54159
54160 //                      Wire the DragZone's handlers up to methods in *this*
54161                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
54162                 }
54163     },
54164
54165 /**     Specify from which ddGroup this DDView accepts drops. */
54166     setDroppable: function(ddGroup) {
54167         if (ddGroup instanceof Array) {
54168                 Roo.each(ddGroup, this.setDroppable, this);
54169                 return;
54170         }
54171         if (this.dropZone) {
54172                 this.dropZone.addToGroup(ddGroup);
54173         } else {
54174                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
54175                                 containerScroll: true,
54176                                 ddGroup: ddGroup
54177                         });
54178
54179 //                      Wire the DropZone's handlers up to methods in *this*
54180                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
54181                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
54182                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
54183                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
54184                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
54185                 }
54186     },
54187
54188 /**     Decide whether to drop above or below a View node. */
54189     getDropPoint : function(e, n, dd){
54190         if (n == this.el.dom) { return "above"; }
54191                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
54192                 var c = t + (b - t) / 2;
54193                 var y = Roo.lib.Event.getPageY(e);
54194                 if(y <= c) {
54195                         return "above";
54196                 }else{
54197                         return "below";
54198                 }
54199     },
54200
54201     onNodeEnter : function(n, dd, e, data){
54202                 return false;
54203     },
54204     
54205     onNodeOver : function(n, dd, e, data){
54206                 var pt = this.getDropPoint(e, n, dd);
54207                 // set the insert point style on the target node
54208                 var dragElClass = this.dropNotAllowed;
54209                 if (pt) {
54210                         var targetElClass;
54211                         if (pt == "above"){
54212                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
54213                                 targetElClass = "x-view-drag-insert-above";
54214                         } else {
54215                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
54216                                 targetElClass = "x-view-drag-insert-below";
54217                         }
54218                         if (this.lastInsertClass != targetElClass){
54219                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
54220                                 this.lastInsertClass = targetElClass;
54221                         }
54222                 }
54223                 return dragElClass;
54224         },
54225
54226     onNodeOut : function(n, dd, e, data){
54227                 this.removeDropIndicators(n);
54228     },
54229
54230     onNodeDrop : function(n, dd, e, data){
54231         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
54232                 return false;
54233         }
54234         var pt = this.getDropPoint(e, n, dd);
54235                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
54236                 if (pt == "below") { insertAt++; }
54237                 for (var i = 0; i < data.records.length; i++) {
54238                         var r = data.records[i];
54239                         var dup = this.store.getById(r.id);
54240                         if (dup && (dd != this.dragZone)) {
54241                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
54242                         } else {
54243                                 if (data.copy) {
54244                                         this.store.insert(insertAt++, r.copy());
54245                                 } else {
54246                                         data.source.isDirtyFlag = true;
54247                                         r.store.remove(r);
54248                                         this.store.insert(insertAt++, r);
54249                                 }
54250                                 this.isDirtyFlag = true;
54251                         }
54252                 }
54253                 this.dragZone.cachedTarget = null;
54254                 return true;
54255     },
54256
54257     removeDropIndicators : function(n){
54258                 if(n){
54259                         Roo.fly(n).removeClass([
54260                                 "x-view-drag-insert-above",
54261                                 "x-view-drag-insert-below"]);
54262                         this.lastInsertClass = "_noclass";
54263                 }
54264     },
54265
54266 /**
54267  *      Utility method. Add a delete option to the DDView's context menu.
54268  *      @param {String} imageUrl The URL of the "delete" icon image.
54269  */
54270         setDeletable: function(imageUrl) {
54271                 if (!this.singleSelect && !this.multiSelect) {
54272                         this.singleSelect = true;
54273                 }
54274                 var c = this.getContextMenu();
54275                 this.contextMenu.on("itemclick", function(item) {
54276                         switch (item.id) {
54277                                 case "delete":
54278                                         this.remove(this.getSelectedIndexes());
54279                                         break;
54280                         }
54281                 }, this);
54282                 this.contextMenu.add({
54283                         icon: imageUrl,
54284                         id: "delete",
54285                         text: 'Delete'
54286                 });
54287         },
54288         
54289 /**     Return the context menu for this DDView. */
54290         getContextMenu: function() {
54291                 if (!this.contextMenu) {
54292 //                      Create the View's context menu
54293                         this.contextMenu = new Roo.menu.Menu({
54294                                 id: this.id + "-contextmenu"
54295                         });
54296                         this.el.on("contextmenu", this.showContextMenu, this);
54297                 }
54298                 return this.contextMenu;
54299         },
54300         
54301         disableContextMenu: function() {
54302                 if (this.contextMenu) {
54303                         this.el.un("contextmenu", this.showContextMenu, this);
54304                 }
54305         },
54306
54307         showContextMenu: function(e, item) {
54308         item = this.findItemFromChild(e.getTarget());
54309                 if (item) {
54310                         e.stopEvent();
54311                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
54312                         this.contextMenu.showAt(e.getXY());
54313             }
54314     },
54315
54316 /**
54317  *      Remove {@link Roo.data.Record}s at the specified indices.
54318  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
54319  */
54320     remove: function(selectedIndices) {
54321                 selectedIndices = [].concat(selectedIndices);
54322                 for (var i = 0; i < selectedIndices.length; i++) {
54323                         var rec = this.store.getAt(selectedIndices[i]);
54324                         this.store.remove(rec);
54325                 }
54326     },
54327
54328 /**
54329  *      Double click fires the event, but also, if this is draggable, and there is only one other
54330  *      related DropZone, it transfers the selected node.
54331  */
54332     onDblClick : function(e){
54333         var item = this.findItemFromChild(e.getTarget());
54334         if(item){
54335             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
54336                 return false;
54337             }
54338             if (this.dragGroup) {
54339                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
54340                     while (targets.indexOf(this.dropZone) > -1) {
54341                             targets.remove(this.dropZone);
54342                                 }
54343                     if (targets.length == 1) {
54344                                         this.dragZone.cachedTarget = null;
54345                         var el = Roo.get(targets[0].getEl());
54346                         var box = el.getBox(true);
54347                         targets[0].onNodeDrop(el.dom, {
54348                                 target: el.dom,
54349                                 xy: [box.x, box.y + box.height - 1]
54350                         }, null, this.getDragData(e));
54351                     }
54352                 }
54353         }
54354     },
54355     
54356     handleSelection: function(e) {
54357                 this.dragZone.cachedTarget = null;
54358         var item = this.findItemFromChild(e.getTarget());
54359         if (!item) {
54360                 this.clearSelections(true);
54361                 return;
54362         }
54363                 if (item && (this.multiSelect || this.singleSelect)){
54364                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
54365                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
54366                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
54367                                 this.unselect(item);
54368                         } else {
54369                                 this.select(item, this.multiSelect && e.ctrlKey);
54370                                 this.lastSelection = item;
54371                         }
54372                 }
54373     },
54374
54375     onItemClick : function(item, index, e){
54376                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
54377                         return false;
54378                 }
54379                 return true;
54380     },
54381
54382     unselect : function(nodeInfo, suppressEvent){
54383                 var node = this.getNode(nodeInfo);
54384                 if(node && this.isSelected(node)){
54385                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
54386                                 Roo.fly(node).removeClass(this.selectedClass);
54387                                 this.selections.remove(node);
54388                                 if(!suppressEvent){
54389                                         this.fireEvent("selectionchange", this, this.selections);
54390                                 }
54391                         }
54392                 }
54393     }
54394 });
54395 /*
54396  * Based on:
54397  * Ext JS Library 1.1.1
54398  * Copyright(c) 2006-2007, Ext JS, LLC.
54399  *
54400  * Originally Released Under LGPL - original licence link has changed is not relivant.
54401  *
54402  * Fork - LGPL
54403  * <script type="text/javascript">
54404  */
54405  
54406 /**
54407  * @class Roo.LayoutManager
54408  * @extends Roo.util.Observable
54409  * Base class for layout managers.
54410  */
54411 Roo.LayoutManager = function(container, config){
54412     Roo.LayoutManager.superclass.constructor.call(this);
54413     this.el = Roo.get(container);
54414     // ie scrollbar fix
54415     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
54416         document.body.scroll = "no";
54417     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
54418         this.el.position('relative');
54419     }
54420     this.id = this.el.id;
54421     this.el.addClass("x-layout-container");
54422     /** false to disable window resize monitoring @type Boolean */
54423     this.monitorWindowResize = true;
54424     this.regions = {};
54425     this.addEvents({
54426         /**
54427          * @event layout
54428          * Fires when a layout is performed. 
54429          * @param {Roo.LayoutManager} this
54430          */
54431         "layout" : true,
54432         /**
54433          * @event regionresized
54434          * Fires when the user resizes a region. 
54435          * @param {Roo.LayoutRegion} region The resized region
54436          * @param {Number} newSize The new size (width for east/west, height for north/south)
54437          */
54438         "regionresized" : true,
54439         /**
54440          * @event regioncollapsed
54441          * Fires when a region is collapsed. 
54442          * @param {Roo.LayoutRegion} region The collapsed region
54443          */
54444         "regioncollapsed" : true,
54445         /**
54446          * @event regionexpanded
54447          * Fires when a region is expanded.  
54448          * @param {Roo.LayoutRegion} region The expanded region
54449          */
54450         "regionexpanded" : true
54451     });
54452     this.updating = false;
54453     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54454 };
54455
54456 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
54457     /**
54458      * Returns true if this layout is currently being updated
54459      * @return {Boolean}
54460      */
54461     isUpdating : function(){
54462         return this.updating; 
54463     },
54464     
54465     /**
54466      * Suspend the LayoutManager from doing auto-layouts while
54467      * making multiple add or remove calls
54468      */
54469     beginUpdate : function(){
54470         this.updating = true;    
54471     },
54472     
54473     /**
54474      * Restore auto-layouts and optionally disable the manager from performing a layout
54475      * @param {Boolean} noLayout true to disable a layout update 
54476      */
54477     endUpdate : function(noLayout){
54478         this.updating = false;
54479         if(!noLayout){
54480             this.layout();
54481         }    
54482     },
54483     
54484     layout: function(){
54485         
54486     },
54487     
54488     onRegionResized : function(region, newSize){
54489         this.fireEvent("regionresized", region, newSize);
54490         this.layout();
54491     },
54492     
54493     onRegionCollapsed : function(region){
54494         this.fireEvent("regioncollapsed", region);
54495     },
54496     
54497     onRegionExpanded : function(region){
54498         this.fireEvent("regionexpanded", region);
54499     },
54500         
54501     /**
54502      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
54503      * performs box-model adjustments.
54504      * @return {Object} The size as an object {width: (the width), height: (the height)}
54505      */
54506     getViewSize : function(){
54507         var size;
54508         if(this.el.dom != document.body){
54509             size = this.el.getSize();
54510         }else{
54511             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
54512         }
54513         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
54514         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
54515         return size;
54516     },
54517     
54518     /**
54519      * Returns the Element this layout is bound to.
54520      * @return {Roo.Element}
54521      */
54522     getEl : function(){
54523         return this.el;
54524     },
54525     
54526     /**
54527      * Returns the specified region.
54528      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
54529      * @return {Roo.LayoutRegion}
54530      */
54531     getRegion : function(target){
54532         return this.regions[target.toLowerCase()];
54533     },
54534     
54535     onWindowResize : function(){
54536         if(this.monitorWindowResize){
54537             this.layout();
54538         }
54539     }
54540 });/*
54541  * Based on:
54542  * Ext JS Library 1.1.1
54543  * Copyright(c) 2006-2007, Ext JS, LLC.
54544  *
54545  * Originally Released Under LGPL - original licence link has changed is not relivant.
54546  *
54547  * Fork - LGPL
54548  * <script type="text/javascript">
54549  */
54550 /**
54551  * @class Roo.BorderLayout
54552  * @extends Roo.LayoutManager
54553  * @children Roo.ContentPanel
54554  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
54555  * please see: <br><br>
54556  * <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>
54557  * <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>
54558  * Example:
54559  <pre><code>
54560  var layout = new Roo.BorderLayout(document.body, {
54561     north: {
54562         initialSize: 25,
54563         titlebar: false
54564     },
54565     west: {
54566         split:true,
54567         initialSize: 200,
54568         minSize: 175,
54569         maxSize: 400,
54570         titlebar: true,
54571         collapsible: true
54572     },
54573     east: {
54574         split:true,
54575         initialSize: 202,
54576         minSize: 175,
54577         maxSize: 400,
54578         titlebar: true,
54579         collapsible: true
54580     },
54581     south: {
54582         split:true,
54583         initialSize: 100,
54584         minSize: 100,
54585         maxSize: 200,
54586         titlebar: true,
54587         collapsible: true
54588     },
54589     center: {
54590         titlebar: true,
54591         autoScroll:true,
54592         resizeTabs: true,
54593         minTabWidth: 50,
54594         preferredTabWidth: 150
54595     }
54596 });
54597
54598 // shorthand
54599 var CP = Roo.ContentPanel;
54600
54601 layout.beginUpdate();
54602 layout.add("north", new CP("north", "North"));
54603 layout.add("south", new CP("south", {title: "South", closable: true}));
54604 layout.add("west", new CP("west", {title: "West"}));
54605 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
54606 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
54607 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
54608 layout.getRegion("center").showPanel("center1");
54609 layout.endUpdate();
54610 </code></pre>
54611
54612 <b>The container the layout is rendered into can be either the body element or any other element.
54613 If it is not the body element, the container needs to either be an absolute positioned element,
54614 or you will need to add "position:relative" to the css of the container.  You will also need to specify
54615 the container size if it is not the body element.</b>
54616
54617 * @constructor
54618 * Create a new BorderLayout
54619 * @param {String/HTMLElement/Element} container The container this layout is bound to
54620 * @param {Object} config Configuration options
54621  */
54622 Roo.BorderLayout = function(container, config){
54623     config = config || {};
54624     Roo.BorderLayout.superclass.constructor.call(this, container, config);
54625     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
54626     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
54627         var target = this.factory.validRegions[i];
54628         if(config[target]){
54629             this.addRegion(target, config[target]);
54630         }
54631     }
54632 };
54633
54634 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
54635         
54636         /**
54637          * @cfg {Roo.LayoutRegion} east
54638          */
54639         /**
54640          * @cfg {Roo.LayoutRegion} west
54641          */
54642         /**
54643          * @cfg {Roo.LayoutRegion} north
54644          */
54645         /**
54646          * @cfg {Roo.LayoutRegion} south
54647          */
54648         /**
54649          * @cfg {Roo.LayoutRegion} center
54650          */
54651     /**
54652      * Creates and adds a new region if it doesn't already exist.
54653      * @param {String} target The target region key (north, south, east, west or center).
54654      * @param {Object} config The regions config object
54655      * @return {BorderLayoutRegion} The new region
54656      */
54657     addRegion : function(target, config){
54658         if(!this.regions[target]){
54659             var r = this.factory.create(target, this, config);
54660             this.bindRegion(target, r);
54661         }
54662         return this.regions[target];
54663     },
54664
54665     // private (kinda)
54666     bindRegion : function(name, r){
54667         this.regions[name] = r;
54668         r.on("visibilitychange", this.layout, this);
54669         r.on("paneladded", this.layout, this);
54670         r.on("panelremoved", this.layout, this);
54671         r.on("invalidated", this.layout, this);
54672         r.on("resized", this.onRegionResized, this);
54673         r.on("collapsed", this.onRegionCollapsed, this);
54674         r.on("expanded", this.onRegionExpanded, this);
54675     },
54676
54677     /**
54678      * Performs a layout update.
54679      */
54680     layout : function(){
54681         if(this.updating) {
54682             return;
54683         }
54684         var size = this.getViewSize();
54685         var w = size.width;
54686         var h = size.height;
54687         var centerW = w;
54688         var centerH = h;
54689         var centerY = 0;
54690         var centerX = 0;
54691         //var x = 0, y = 0;
54692
54693         var rs = this.regions;
54694         var north = rs["north"];
54695         var south = rs["south"]; 
54696         var west = rs["west"];
54697         var east = rs["east"];
54698         var center = rs["center"];
54699         //if(this.hideOnLayout){ // not supported anymore
54700             //c.el.setStyle("display", "none");
54701         //}
54702         if(north && north.isVisible()){
54703             var b = north.getBox();
54704             var m = north.getMargins();
54705             b.width = w - (m.left+m.right);
54706             b.x = m.left;
54707             b.y = m.top;
54708             centerY = b.height + b.y + m.bottom;
54709             centerH -= centerY;
54710             north.updateBox(this.safeBox(b));
54711         }
54712         if(south && south.isVisible()){
54713             var b = south.getBox();
54714             var m = south.getMargins();
54715             b.width = w - (m.left+m.right);
54716             b.x = m.left;
54717             var totalHeight = (b.height + m.top + m.bottom);
54718             b.y = h - totalHeight + m.top;
54719             centerH -= totalHeight;
54720             south.updateBox(this.safeBox(b));
54721         }
54722         if(west && west.isVisible()){
54723             var b = west.getBox();
54724             var m = west.getMargins();
54725             b.height = centerH - (m.top+m.bottom);
54726             b.x = m.left;
54727             b.y = centerY + m.top;
54728             var totalWidth = (b.width + m.left + m.right);
54729             centerX += totalWidth;
54730             centerW -= totalWidth;
54731             west.updateBox(this.safeBox(b));
54732         }
54733         if(east && east.isVisible()){
54734             var b = east.getBox();
54735             var m = east.getMargins();
54736             b.height = centerH - (m.top+m.bottom);
54737             var totalWidth = (b.width + m.left + m.right);
54738             b.x = w - totalWidth + m.left;
54739             b.y = centerY + m.top;
54740             centerW -= totalWidth;
54741             east.updateBox(this.safeBox(b));
54742         }
54743         if(center){
54744             var m = center.getMargins();
54745             var centerBox = {
54746                 x: centerX + m.left,
54747                 y: centerY + m.top,
54748                 width: centerW - (m.left+m.right),
54749                 height: centerH - (m.top+m.bottom)
54750             };
54751             //if(this.hideOnLayout){
54752                 //center.el.setStyle("display", "block");
54753             //}
54754             center.updateBox(this.safeBox(centerBox));
54755         }
54756         this.el.repaint();
54757         this.fireEvent("layout", this);
54758     },
54759
54760     // private
54761     safeBox : function(box){
54762         box.width = Math.max(0, box.width);
54763         box.height = Math.max(0, box.height);
54764         return box;
54765     },
54766
54767     /**
54768      * Adds a ContentPanel (or subclass) to this layout.
54769      * @param {String} target The target region key (north, south, east, west or center).
54770      * @param {Roo.ContentPanel} panel The panel to add
54771      * @return {Roo.ContentPanel} The added panel
54772      */
54773     add : function(target, panel){
54774          
54775         target = target.toLowerCase();
54776         return this.regions[target].add(panel);
54777     },
54778
54779     /**
54780      * Remove a ContentPanel (or subclass) to this layout.
54781      * @param {String} target The target region key (north, south, east, west or center).
54782      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
54783      * @return {Roo.ContentPanel} The removed panel
54784      */
54785     remove : function(target, panel){
54786         target = target.toLowerCase();
54787         return this.regions[target].remove(panel);
54788     },
54789
54790     /**
54791      * Searches all regions for a panel with the specified id
54792      * @param {String} panelId
54793      * @return {Roo.ContentPanel} The panel or null if it wasn't found
54794      */
54795     findPanel : function(panelId){
54796         var rs = this.regions;
54797         for(var target in rs){
54798             if(typeof rs[target] != "function"){
54799                 var p = rs[target].getPanel(panelId);
54800                 if(p){
54801                     return p;
54802                 }
54803             }
54804         }
54805         return null;
54806     },
54807
54808     /**
54809      * Searches all regions for a panel with the specified id and activates (shows) it.
54810      * @param {String/ContentPanel} panelId The panels id or the panel itself
54811      * @return {Roo.ContentPanel} The shown panel or null
54812      */
54813     showPanel : function(panelId) {
54814       var rs = this.regions;
54815       for(var target in rs){
54816          var r = rs[target];
54817          if(typeof r != "function"){
54818             if(r.hasPanel(panelId)){
54819                return r.showPanel(panelId);
54820             }
54821          }
54822       }
54823       return null;
54824    },
54825
54826    /**
54827      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
54828      * @param {Roo.state.Provider} provider (optional) An alternate state provider
54829      */
54830     restoreState : function(provider){
54831         if(!provider){
54832             provider = Roo.state.Manager;
54833         }
54834         var sm = new Roo.LayoutStateManager();
54835         sm.init(this, provider);
54836     },
54837
54838     /**
54839      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
54840      * object should contain properties for each region to add ContentPanels to, and each property's value should be
54841      * a valid ContentPanel config object.  Example:
54842      * <pre><code>
54843 // Create the main layout
54844 var layout = new Roo.BorderLayout('main-ct', {
54845     west: {
54846         split:true,
54847         minSize: 175,
54848         titlebar: true
54849     },
54850     center: {
54851         title:'Components'
54852     }
54853 }, 'main-ct');
54854
54855 // Create and add multiple ContentPanels at once via configs
54856 layout.batchAdd({
54857    west: {
54858        id: 'source-files',
54859        autoCreate:true,
54860        title:'Ext Source Files',
54861        autoScroll:true,
54862        fitToFrame:true
54863    },
54864    center : {
54865        el: cview,
54866        autoScroll:true,
54867        fitToFrame:true,
54868        toolbar: tb,
54869        resizeEl:'cbody'
54870    }
54871 });
54872 </code></pre>
54873      * @param {Object} regions An object containing ContentPanel configs by region name
54874      */
54875     batchAdd : function(regions){
54876         this.beginUpdate();
54877         for(var rname in regions){
54878             var lr = this.regions[rname];
54879             if(lr){
54880                 this.addTypedPanels(lr, regions[rname]);
54881             }
54882         }
54883         this.endUpdate();
54884     },
54885
54886     // private
54887     addTypedPanels : function(lr, ps){
54888         if(typeof ps == 'string'){
54889             lr.add(new Roo.ContentPanel(ps));
54890         }
54891         else if(ps instanceof Array){
54892             for(var i =0, len = ps.length; i < len; i++){
54893                 this.addTypedPanels(lr, ps[i]);
54894             }
54895         }
54896         else if(!ps.events){ // raw config?
54897             var el = ps.el;
54898             delete ps.el; // prevent conflict
54899             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
54900         }
54901         else {  // panel object assumed!
54902             lr.add(ps);
54903         }
54904     },
54905     /**
54906      * Adds a xtype elements to the layout.
54907      * <pre><code>
54908
54909 layout.addxtype({
54910        xtype : 'ContentPanel',
54911        region: 'west',
54912        items: [ .... ]
54913    }
54914 );
54915
54916 layout.addxtype({
54917         xtype : 'NestedLayoutPanel',
54918         region: 'west',
54919         layout: {
54920            center: { },
54921            west: { }   
54922         },
54923         items : [ ... list of content panels or nested layout panels.. ]
54924    }
54925 );
54926 </code></pre>
54927      * @param {Object} cfg Xtype definition of item to add.
54928      */
54929     addxtype : function(cfg)
54930     {
54931         // basically accepts a pannel...
54932         // can accept a layout region..!?!?
54933         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
54934         
54935         if (!cfg.xtype.match(/Panel$/)) {
54936             return false;
54937         }
54938         var ret = false;
54939         
54940         if (typeof(cfg.region) == 'undefined') {
54941             Roo.log("Failed to add Panel, region was not set");
54942             Roo.log(cfg);
54943             return false;
54944         }
54945         var region = cfg.region;
54946         delete cfg.region;
54947         
54948           
54949         var xitems = [];
54950         if (cfg.items) {
54951             xitems = cfg.items;
54952             delete cfg.items;
54953         }
54954         var nb = false;
54955         
54956         switch(cfg.xtype) 
54957         {
54958             case 'ContentPanel':  // ContentPanel (el, cfg)
54959             case 'ScrollPanel':  // ContentPanel (el, cfg)
54960             case 'ViewPanel': 
54961                 if(cfg.autoCreate) {
54962                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54963                 } else {
54964                     var el = this.el.createChild();
54965                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
54966                 }
54967                 
54968                 this.add(region, ret);
54969                 break;
54970             
54971             
54972             case 'TreePanel': // our new panel!
54973                 cfg.el = this.el.createChild();
54974                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54975                 this.add(region, ret);
54976                 break;
54977             
54978             case 'NestedLayoutPanel': 
54979                 // create a new Layout (which is  a Border Layout...
54980                 var el = this.el.createChild();
54981                 var clayout = cfg.layout;
54982                 delete cfg.layout;
54983                 clayout.items   = clayout.items  || [];
54984                 // replace this exitems with the clayout ones..
54985                 xitems = clayout.items;
54986                  
54987                 
54988                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
54989                     cfg.background = false;
54990                 }
54991                 var layout = new Roo.BorderLayout(el, clayout);
54992                 
54993                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
54994                 //console.log('adding nested layout panel '  + cfg.toSource());
54995                 this.add(region, ret);
54996                 nb = {}; /// find first...
54997                 break;
54998                 
54999             case 'GridPanel': 
55000             
55001                 // needs grid and region
55002                 
55003                 //var el = this.getRegion(region).el.createChild();
55004                 var el = this.el.createChild();
55005                 // create the grid first...
55006                 
55007                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
55008                 delete cfg.grid;
55009                 if (region == 'center' && this.active ) {
55010                     cfg.background = false;
55011                 }
55012                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
55013                 
55014                 this.add(region, ret);
55015                 if (cfg.background) {
55016                     ret.on('activate', function(gp) {
55017                         if (!gp.grid.rendered) {
55018                             gp.grid.render();
55019                         }
55020                     });
55021                 } else {
55022                     grid.render();
55023                 }
55024                 break;
55025            
55026            
55027            
55028                 
55029                 
55030                 
55031             default:
55032                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
55033                     
55034                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55035                     this.add(region, ret);
55036                 } else {
55037                 
55038                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
55039                     return null;
55040                 }
55041                 
55042              // GridPanel (grid, cfg)
55043             
55044         }
55045         this.beginUpdate();
55046         // add children..
55047         var region = '';
55048         var abn = {};
55049         Roo.each(xitems, function(i)  {
55050             region = nb && i.region ? i.region : false;
55051             
55052             var add = ret.addxtype(i);
55053            
55054             if (region) {
55055                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
55056                 if (!i.background) {
55057                     abn[region] = nb[region] ;
55058                 }
55059             }
55060             
55061         });
55062         this.endUpdate();
55063
55064         // make the last non-background panel active..
55065         //if (nb) { Roo.log(abn); }
55066         if (nb) {
55067             
55068             for(var r in abn) {
55069                 region = this.getRegion(r);
55070                 if (region) {
55071                     // tried using nb[r], but it does not work..
55072                      
55073                     region.showPanel(abn[r]);
55074                    
55075                 }
55076             }
55077         }
55078         return ret;
55079         
55080     }
55081 });
55082
55083 /**
55084  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
55085  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
55086  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
55087  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
55088  * <pre><code>
55089 // shorthand
55090 var CP = Roo.ContentPanel;
55091
55092 var layout = Roo.BorderLayout.create({
55093     north: {
55094         initialSize: 25,
55095         titlebar: false,
55096         panels: [new CP("north", "North")]
55097     },
55098     west: {
55099         split:true,
55100         initialSize: 200,
55101         minSize: 175,
55102         maxSize: 400,
55103         titlebar: true,
55104         collapsible: true,
55105         panels: [new CP("west", {title: "West"})]
55106     },
55107     east: {
55108         split:true,
55109         initialSize: 202,
55110         minSize: 175,
55111         maxSize: 400,
55112         titlebar: true,
55113         collapsible: true,
55114         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
55115     },
55116     south: {
55117         split:true,
55118         initialSize: 100,
55119         minSize: 100,
55120         maxSize: 200,
55121         titlebar: true,
55122         collapsible: true,
55123         panels: [new CP("south", {title: "South", closable: true})]
55124     },
55125     center: {
55126         titlebar: true,
55127         autoScroll:true,
55128         resizeTabs: true,
55129         minTabWidth: 50,
55130         preferredTabWidth: 150,
55131         panels: [
55132             new CP("center1", {title: "Close Me", closable: true}),
55133             new CP("center2", {title: "Center Panel", closable: false})
55134         ]
55135     }
55136 }, document.body);
55137
55138 layout.getRegion("center").showPanel("center1");
55139 </code></pre>
55140  * @param config
55141  * @param targetEl
55142  */
55143 Roo.BorderLayout.create = function(config, targetEl){
55144     var layout = new Roo.BorderLayout(targetEl || document.body, config);
55145     layout.beginUpdate();
55146     var regions = Roo.BorderLayout.RegionFactory.validRegions;
55147     for(var j = 0, jlen = regions.length; j < jlen; j++){
55148         var lr = regions[j];
55149         if(layout.regions[lr] && config[lr].panels){
55150             var r = layout.regions[lr];
55151             var ps = config[lr].panels;
55152             layout.addTypedPanels(r, ps);
55153         }
55154     }
55155     layout.endUpdate();
55156     return layout;
55157 };
55158
55159 // private
55160 Roo.BorderLayout.RegionFactory = {
55161     // private
55162     validRegions : ["north","south","east","west","center"],
55163
55164     // private
55165     create : function(target, mgr, config){
55166         target = target.toLowerCase();
55167         if(config.lightweight || config.basic){
55168             return new Roo.BasicLayoutRegion(mgr, config, target);
55169         }
55170         switch(target){
55171             case "north":
55172                 return new Roo.NorthLayoutRegion(mgr, config);
55173             case "south":
55174                 return new Roo.SouthLayoutRegion(mgr, config);
55175             case "east":
55176                 return new Roo.EastLayoutRegion(mgr, config);
55177             case "west":
55178                 return new Roo.WestLayoutRegion(mgr, config);
55179             case "center":
55180                 return new Roo.CenterLayoutRegion(mgr, config);
55181         }
55182         throw 'Layout region "'+target+'" not supported.';
55183     }
55184 };/*
55185  * Based on:
55186  * Ext JS Library 1.1.1
55187  * Copyright(c) 2006-2007, Ext JS, LLC.
55188  *
55189  * Originally Released Under LGPL - original licence link has changed is not relivant.
55190  *
55191  * Fork - LGPL
55192  * <script type="text/javascript">
55193  */
55194  
55195 /**
55196  * @class Roo.BasicLayoutRegion
55197  * @extends Roo.util.Observable
55198  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
55199  * and does not have a titlebar, tabs or any other features. All it does is size and position 
55200  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
55201  */
55202 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
55203     this.mgr = mgr;
55204     this.position  = pos;
55205     this.events = {
55206         /**
55207          * @scope Roo.BasicLayoutRegion
55208          */
55209         
55210         /**
55211          * @event beforeremove
55212          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
55213          * @param {Roo.LayoutRegion} this
55214          * @param {Roo.ContentPanel} panel The panel
55215          * @param {Object} e The cancel event object
55216          */
55217         "beforeremove" : true,
55218         /**
55219          * @event invalidated
55220          * Fires when the layout for this region is changed.
55221          * @param {Roo.LayoutRegion} this
55222          */
55223         "invalidated" : true,
55224         /**
55225          * @event visibilitychange
55226          * Fires when this region is shown or hidden 
55227          * @param {Roo.LayoutRegion} this
55228          * @param {Boolean} visibility true or false
55229          */
55230         "visibilitychange" : true,
55231         /**
55232          * @event paneladded
55233          * Fires when a panel is added. 
55234          * @param {Roo.LayoutRegion} this
55235          * @param {Roo.ContentPanel} panel The panel
55236          */
55237         "paneladded" : true,
55238         /**
55239          * @event panelremoved
55240          * Fires when a panel is removed. 
55241          * @param {Roo.LayoutRegion} this
55242          * @param {Roo.ContentPanel} panel The panel
55243          */
55244         "panelremoved" : true,
55245         /**
55246          * @event beforecollapse
55247          * Fires when this region before collapse.
55248          * @param {Roo.LayoutRegion} this
55249          */
55250         "beforecollapse" : true,
55251         /**
55252          * @event collapsed
55253          * Fires when this region is collapsed.
55254          * @param {Roo.LayoutRegion} this
55255          */
55256         "collapsed" : true,
55257         /**
55258          * @event expanded
55259          * Fires when this region is expanded.
55260          * @param {Roo.LayoutRegion} this
55261          */
55262         "expanded" : true,
55263         /**
55264          * @event slideshow
55265          * Fires when this region is slid into view.
55266          * @param {Roo.LayoutRegion} this
55267          */
55268         "slideshow" : true,
55269         /**
55270          * @event slidehide
55271          * Fires when this region slides out of view. 
55272          * @param {Roo.LayoutRegion} this
55273          */
55274         "slidehide" : true,
55275         /**
55276          * @event panelactivated
55277          * Fires when a panel is activated. 
55278          * @param {Roo.LayoutRegion} this
55279          * @param {Roo.ContentPanel} panel The activated panel
55280          */
55281         "panelactivated" : true,
55282         /**
55283          * @event resized
55284          * Fires when the user resizes this region. 
55285          * @param {Roo.LayoutRegion} this
55286          * @param {Number} newSize The new size (width for east/west, height for north/south)
55287          */
55288         "resized" : true
55289     };
55290     /** A collection of panels in this region. @type Roo.util.MixedCollection */
55291     this.panels = new Roo.util.MixedCollection();
55292     this.panels.getKey = this.getPanelId.createDelegate(this);
55293     this.box = null;
55294     this.activePanel = null;
55295     // ensure listeners are added...
55296     
55297     if (config.listeners || config.events) {
55298         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
55299             listeners : config.listeners || {},
55300             events : config.events || {}
55301         });
55302     }
55303     
55304     if(skipConfig !== true){
55305         this.applyConfig(config);
55306     }
55307 };
55308
55309 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
55310     getPanelId : function(p){
55311         return p.getId();
55312     },
55313     
55314     applyConfig : function(config){
55315         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55316         this.config = config;
55317         
55318     },
55319     
55320     /**
55321      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
55322      * the width, for horizontal (north, south) the height.
55323      * @param {Number} newSize The new width or height
55324      */
55325     resizeTo : function(newSize){
55326         var el = this.el ? this.el :
55327                  (this.activePanel ? this.activePanel.getEl() : null);
55328         if(el){
55329             switch(this.position){
55330                 case "east":
55331                 case "west":
55332                     el.setWidth(newSize);
55333                     this.fireEvent("resized", this, newSize);
55334                 break;
55335                 case "north":
55336                 case "south":
55337                     el.setHeight(newSize);
55338                     this.fireEvent("resized", this, newSize);
55339                 break;                
55340             }
55341         }
55342     },
55343     
55344     getBox : function(){
55345         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
55346     },
55347     
55348     getMargins : function(){
55349         return this.margins;
55350     },
55351     
55352     updateBox : function(box){
55353         this.box = box;
55354         var el = this.activePanel.getEl();
55355         el.dom.style.left = box.x + "px";
55356         el.dom.style.top = box.y + "px";
55357         this.activePanel.setSize(box.width, box.height);
55358     },
55359     
55360     /**
55361      * Returns the container element for this region.
55362      * @return {Roo.Element}
55363      */
55364     getEl : function(){
55365         return this.activePanel;
55366     },
55367     
55368     /**
55369      * Returns true if this region is currently visible.
55370      * @return {Boolean}
55371      */
55372     isVisible : function(){
55373         return this.activePanel ? true : false;
55374     },
55375     
55376     setActivePanel : function(panel){
55377         panel = this.getPanel(panel);
55378         if(this.activePanel && this.activePanel != panel){
55379             this.activePanel.setActiveState(false);
55380             this.activePanel.getEl().setLeftTop(-10000,-10000);
55381         }
55382         this.activePanel = panel;
55383         panel.setActiveState(true);
55384         if(this.box){
55385             panel.setSize(this.box.width, this.box.height);
55386         }
55387         this.fireEvent("panelactivated", this, panel);
55388         this.fireEvent("invalidated");
55389     },
55390     
55391     /**
55392      * Show the specified panel.
55393      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
55394      * @return {Roo.ContentPanel} The shown panel or null
55395      */
55396     showPanel : function(panel){
55397         if(panel = this.getPanel(panel)){
55398             this.setActivePanel(panel);
55399         }
55400         return panel;
55401     },
55402     
55403     /**
55404      * Get the active panel for this region.
55405      * @return {Roo.ContentPanel} The active panel or null
55406      */
55407     getActivePanel : function(){
55408         return this.activePanel;
55409     },
55410     
55411     /**
55412      * Add the passed ContentPanel(s)
55413      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55414      * @return {Roo.ContentPanel} The panel added (if only one was added)
55415      */
55416     add : function(panel){
55417         if(arguments.length > 1){
55418             for(var i = 0, len = arguments.length; i < len; i++) {
55419                 this.add(arguments[i]);
55420             }
55421             return null;
55422         }
55423         if(this.hasPanel(panel)){
55424             this.showPanel(panel);
55425             return panel;
55426         }
55427         var el = panel.getEl();
55428         if(el.dom.parentNode != this.mgr.el.dom){
55429             this.mgr.el.dom.appendChild(el.dom);
55430         }
55431         if(panel.setRegion){
55432             panel.setRegion(this);
55433         }
55434         this.panels.add(panel);
55435         el.setStyle("position", "absolute");
55436         if(!panel.background){
55437             this.setActivePanel(panel);
55438             if(this.config.initialSize && this.panels.getCount()==1){
55439                 this.resizeTo(this.config.initialSize);
55440             }
55441         }
55442         this.fireEvent("paneladded", this, panel);
55443         return panel;
55444     },
55445     
55446     /**
55447      * Returns true if the panel is in this region.
55448      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55449      * @return {Boolean}
55450      */
55451     hasPanel : function(panel){
55452         if(typeof panel == "object"){ // must be panel obj
55453             panel = panel.getId();
55454         }
55455         return this.getPanel(panel) ? true : false;
55456     },
55457     
55458     /**
55459      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55460      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55461      * @param {Boolean} preservePanel Overrides the config preservePanel option
55462      * @return {Roo.ContentPanel} The panel that was removed
55463      */
55464     remove : function(panel, preservePanel){
55465         panel = this.getPanel(panel);
55466         if(!panel){
55467             return null;
55468         }
55469         var e = {};
55470         this.fireEvent("beforeremove", this, panel, e);
55471         if(e.cancel === true){
55472             return null;
55473         }
55474         var panelId = panel.getId();
55475         this.panels.removeKey(panelId);
55476         return panel;
55477     },
55478     
55479     /**
55480      * Returns the panel specified or null if it's not in this region.
55481      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55482      * @return {Roo.ContentPanel}
55483      */
55484     getPanel : function(id){
55485         if(typeof id == "object"){ // must be panel obj
55486             return id;
55487         }
55488         return this.panels.get(id);
55489     },
55490     
55491     /**
55492      * Returns this regions position (north/south/east/west/center).
55493      * @return {String} 
55494      */
55495     getPosition: function(){
55496         return this.position;    
55497     }
55498 });/*
55499  * Based on:
55500  * Ext JS Library 1.1.1
55501  * Copyright(c) 2006-2007, Ext JS, LLC.
55502  *
55503  * Originally Released Under LGPL - original licence link has changed is not relivant.
55504  *
55505  * Fork - LGPL
55506  * <script type="text/javascript">
55507  */
55508  
55509 /**
55510  * @class Roo.LayoutRegion
55511  * @extends Roo.BasicLayoutRegion
55512  * This class represents a region in a layout manager.
55513  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
55514  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
55515  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
55516  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
55517  * @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})
55518  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
55519  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
55520  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
55521  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
55522  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
55523  * @cfg {String}    title           The title for the region (overrides panel titles)
55524  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
55525  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
55526  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
55527  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
55528  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
55529  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
55530  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
55531  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
55532  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
55533  * @cfg {Boolean}   showPin         True to show a pin button
55534  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
55535  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
55536  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
55537  * @cfg {Number}    width           For East/West panels
55538  * @cfg {Number}    height          For North/South panels
55539  * @cfg {Boolean}   split           To show the splitter
55540  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
55541  */
55542 Roo.LayoutRegion = function(mgr, config, pos){
55543     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
55544     var dh = Roo.DomHelper;
55545     /** This region's container element 
55546     * @type Roo.Element */
55547     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
55548     /** This region's title element 
55549     * @type Roo.Element */
55550
55551     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
55552         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
55553         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
55554     ]}, true);
55555     this.titleEl.enableDisplayMode();
55556     /** This region's title text element 
55557     * @type HTMLElement */
55558     this.titleTextEl = this.titleEl.dom.firstChild;
55559     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
55560     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
55561     this.closeBtn.enableDisplayMode();
55562     this.closeBtn.on("click", this.closeClicked, this);
55563     this.closeBtn.hide();
55564
55565     this.createBody(config);
55566     this.visible = true;
55567     this.collapsed = false;
55568
55569     if(config.hideWhenEmpty){
55570         this.hide();
55571         this.on("paneladded", this.validateVisibility, this);
55572         this.on("panelremoved", this.validateVisibility, this);
55573     }
55574     this.applyConfig(config);
55575 };
55576
55577 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
55578
55579     createBody : function(){
55580         /** This region's body element 
55581         * @type Roo.Element */
55582         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
55583     },
55584
55585     applyConfig : function(c){
55586         if(c.collapsible && this.position != "center" && !this.collapsedEl){
55587             var dh = Roo.DomHelper;
55588             if(c.titlebar !== false){
55589                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
55590                 this.collapseBtn.on("click", this.collapse, this);
55591                 this.collapseBtn.enableDisplayMode();
55592
55593                 if(c.showPin === true || this.showPin){
55594                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
55595                     this.stickBtn.enableDisplayMode();
55596                     this.stickBtn.on("click", this.expand, this);
55597                     this.stickBtn.hide();
55598                 }
55599             }
55600             /** This region's collapsed element
55601             * @type Roo.Element */
55602             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
55603                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
55604             ]}, true);
55605             if(c.floatable !== false){
55606                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
55607                this.collapsedEl.on("click", this.collapseClick, this);
55608             }
55609
55610             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
55611                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
55612                    id: "message", unselectable: "on", style:{"float":"left"}});
55613                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
55614              }
55615             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
55616             this.expandBtn.on("click", this.expand, this);
55617         }
55618         if(this.collapseBtn){
55619             this.collapseBtn.setVisible(c.collapsible == true);
55620         }
55621         this.cmargins = c.cmargins || this.cmargins ||
55622                          (this.position == "west" || this.position == "east" ?
55623                              {top: 0, left: 2, right:2, bottom: 0} :
55624                              {top: 2, left: 0, right:0, bottom: 2});
55625         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55626         this.bottomTabs = c.tabPosition != "top";
55627         this.autoScroll = c.autoScroll || false;
55628         if(this.autoScroll){
55629             this.bodyEl.setStyle("overflow", "auto");
55630         }else{
55631             this.bodyEl.setStyle("overflow", "hidden");
55632         }
55633         //if(c.titlebar !== false){
55634             if((!c.titlebar && !c.title) || c.titlebar === false){
55635                 this.titleEl.hide();
55636             }else{
55637                 this.titleEl.show();
55638                 if(c.title){
55639                     this.titleTextEl.innerHTML = c.title;
55640                 }
55641             }
55642         //}
55643         this.duration = c.duration || .30;
55644         this.slideDuration = c.slideDuration || .45;
55645         this.config = c;
55646         if(c.collapsed){
55647             this.collapse(true);
55648         }
55649         if(c.hidden){
55650             this.hide();
55651         }
55652     },
55653     /**
55654      * Returns true if this region is currently visible.
55655      * @return {Boolean}
55656      */
55657     isVisible : function(){
55658         return this.visible;
55659     },
55660
55661     /**
55662      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
55663      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
55664      */
55665     setCollapsedTitle : function(title){
55666         title = title || "&#160;";
55667         if(this.collapsedTitleTextEl){
55668             this.collapsedTitleTextEl.innerHTML = title;
55669         }
55670     },
55671
55672     getBox : function(){
55673         var b;
55674         if(!this.collapsed){
55675             b = this.el.getBox(false, true);
55676         }else{
55677             b = this.collapsedEl.getBox(false, true);
55678         }
55679         return b;
55680     },
55681
55682     getMargins : function(){
55683         return this.collapsed ? this.cmargins : this.margins;
55684     },
55685
55686     highlight : function(){
55687         this.el.addClass("x-layout-panel-dragover");
55688     },
55689
55690     unhighlight : function(){
55691         this.el.removeClass("x-layout-panel-dragover");
55692     },
55693
55694     updateBox : function(box){
55695         this.box = box;
55696         if(!this.collapsed){
55697             this.el.dom.style.left = box.x + "px";
55698             this.el.dom.style.top = box.y + "px";
55699             this.updateBody(box.width, box.height);
55700         }else{
55701             this.collapsedEl.dom.style.left = box.x + "px";
55702             this.collapsedEl.dom.style.top = box.y + "px";
55703             this.collapsedEl.setSize(box.width, box.height);
55704         }
55705         if(this.tabs){
55706             this.tabs.autoSizeTabs();
55707         }
55708     },
55709
55710     updateBody : function(w, h){
55711         if(w !== null){
55712             this.el.setWidth(w);
55713             w -= this.el.getBorderWidth("rl");
55714             if(this.config.adjustments){
55715                 w += this.config.adjustments[0];
55716             }
55717         }
55718         if(h !== null){
55719             this.el.setHeight(h);
55720             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
55721             h -= this.el.getBorderWidth("tb");
55722             if(this.config.adjustments){
55723                 h += this.config.adjustments[1];
55724             }
55725             this.bodyEl.setHeight(h);
55726             if(this.tabs){
55727                 h = this.tabs.syncHeight(h);
55728             }
55729         }
55730         if(this.panelSize){
55731             w = w !== null ? w : this.panelSize.width;
55732             h = h !== null ? h : this.panelSize.height;
55733         }
55734         if(this.activePanel){
55735             var el = this.activePanel.getEl();
55736             w = w !== null ? w : el.getWidth();
55737             h = h !== null ? h : el.getHeight();
55738             this.panelSize = {width: w, height: h};
55739             this.activePanel.setSize(w, h);
55740         }
55741         if(Roo.isIE && this.tabs){
55742             this.tabs.el.repaint();
55743         }
55744     },
55745
55746     /**
55747      * Returns the container element for this region.
55748      * @return {Roo.Element}
55749      */
55750     getEl : function(){
55751         return this.el;
55752     },
55753
55754     /**
55755      * Hides this region.
55756      */
55757     hide : function(){
55758         if(!this.collapsed){
55759             this.el.dom.style.left = "-2000px";
55760             this.el.hide();
55761         }else{
55762             this.collapsedEl.dom.style.left = "-2000px";
55763             this.collapsedEl.hide();
55764         }
55765         this.visible = false;
55766         this.fireEvent("visibilitychange", this, false);
55767     },
55768
55769     /**
55770      * Shows this region if it was previously hidden.
55771      */
55772     show : function(){
55773         if(!this.collapsed){
55774             this.el.show();
55775         }else{
55776             this.collapsedEl.show();
55777         }
55778         this.visible = true;
55779         this.fireEvent("visibilitychange", this, true);
55780     },
55781
55782     closeClicked : function(){
55783         if(this.activePanel){
55784             this.remove(this.activePanel);
55785         }
55786     },
55787
55788     collapseClick : function(e){
55789         if(this.isSlid){
55790            e.stopPropagation();
55791            this.slideIn();
55792         }else{
55793            e.stopPropagation();
55794            this.slideOut();
55795         }
55796     },
55797
55798     /**
55799      * Collapses this region.
55800      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
55801      */
55802     collapse : function(skipAnim, skipCheck){
55803         if(this.collapsed) {
55804             return;
55805         }
55806         
55807         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
55808             
55809             this.collapsed = true;
55810             if(this.split){
55811                 this.split.el.hide();
55812             }
55813             if(this.config.animate && skipAnim !== true){
55814                 this.fireEvent("invalidated", this);
55815                 this.animateCollapse();
55816             }else{
55817                 this.el.setLocation(-20000,-20000);
55818                 this.el.hide();
55819                 this.collapsedEl.show();
55820                 this.fireEvent("collapsed", this);
55821                 this.fireEvent("invalidated", this);
55822             }
55823         }
55824         
55825     },
55826
55827     animateCollapse : function(){
55828         // overridden
55829     },
55830
55831     /**
55832      * Expands this region if it was previously collapsed.
55833      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
55834      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
55835      */
55836     expand : function(e, skipAnim){
55837         if(e) {
55838             e.stopPropagation();
55839         }
55840         if(!this.collapsed || this.el.hasActiveFx()) {
55841             return;
55842         }
55843         if(this.isSlid){
55844             this.afterSlideIn();
55845             skipAnim = true;
55846         }
55847         this.collapsed = false;
55848         if(this.config.animate && skipAnim !== true){
55849             this.animateExpand();
55850         }else{
55851             this.el.show();
55852             if(this.split){
55853                 this.split.el.show();
55854             }
55855             this.collapsedEl.setLocation(-2000,-2000);
55856             this.collapsedEl.hide();
55857             this.fireEvent("invalidated", this);
55858             this.fireEvent("expanded", this);
55859         }
55860     },
55861
55862     animateExpand : function(){
55863         // overridden
55864     },
55865
55866     initTabs : function()
55867     {
55868         this.bodyEl.setStyle("overflow", "hidden");
55869         var ts = new Roo.TabPanel(
55870                 this.bodyEl.dom,
55871                 {
55872                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
55873                     disableTooltips: this.config.disableTabTips,
55874                     toolbar : this.config.toolbar
55875                 }
55876         );
55877         if(this.config.hideTabs){
55878             ts.stripWrap.setDisplayed(false);
55879         }
55880         this.tabs = ts;
55881         ts.resizeTabs = this.config.resizeTabs === true;
55882         ts.minTabWidth = this.config.minTabWidth || 40;
55883         ts.maxTabWidth = this.config.maxTabWidth || 250;
55884         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
55885         ts.monitorResize = false;
55886         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
55887         ts.bodyEl.addClass('x-layout-tabs-body');
55888         this.panels.each(this.initPanelAsTab, this);
55889     },
55890
55891     initPanelAsTab : function(panel){
55892         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
55893                     this.config.closeOnTab && panel.isClosable());
55894         if(panel.tabTip !== undefined){
55895             ti.setTooltip(panel.tabTip);
55896         }
55897         ti.on("activate", function(){
55898               this.setActivePanel(panel);
55899         }, this);
55900         if(this.config.closeOnTab){
55901             ti.on("beforeclose", function(t, e){
55902                 e.cancel = true;
55903                 this.remove(panel);
55904             }, this);
55905         }
55906         return ti;
55907     },
55908
55909     updatePanelTitle : function(panel, title){
55910         if(this.activePanel == panel){
55911             this.updateTitle(title);
55912         }
55913         if(this.tabs){
55914             var ti = this.tabs.getTab(panel.getEl().id);
55915             ti.setText(title);
55916             if(panel.tabTip !== undefined){
55917                 ti.setTooltip(panel.tabTip);
55918             }
55919         }
55920     },
55921
55922     updateTitle : function(title){
55923         if(this.titleTextEl && !this.config.title){
55924             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
55925         }
55926     },
55927
55928     setActivePanel : function(panel){
55929         panel = this.getPanel(panel);
55930         if(this.activePanel && this.activePanel != panel){
55931             this.activePanel.setActiveState(false);
55932         }
55933         this.activePanel = panel;
55934         panel.setActiveState(true);
55935         if(this.panelSize){
55936             panel.setSize(this.panelSize.width, this.panelSize.height);
55937         }
55938         if(this.closeBtn){
55939             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
55940         }
55941         this.updateTitle(panel.getTitle());
55942         if(this.tabs){
55943             this.fireEvent("invalidated", this);
55944         }
55945         this.fireEvent("panelactivated", this, panel);
55946     },
55947
55948     /**
55949      * Shows the specified panel.
55950      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
55951      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
55952      */
55953     showPanel : function(panel)
55954     {
55955         panel = this.getPanel(panel);
55956         if(panel){
55957             if(this.tabs){
55958                 var tab = this.tabs.getTab(panel.getEl().id);
55959                 if(tab.isHidden()){
55960                     this.tabs.unhideTab(tab.id);
55961                 }
55962                 tab.activate();
55963             }else{
55964                 this.setActivePanel(panel);
55965             }
55966         }
55967         return panel;
55968     },
55969
55970     /**
55971      * Get the active panel for this region.
55972      * @return {Roo.ContentPanel} The active panel or null
55973      */
55974     getActivePanel : function(){
55975         return this.activePanel;
55976     },
55977
55978     validateVisibility : function(){
55979         if(this.panels.getCount() < 1){
55980             this.updateTitle("&#160;");
55981             this.closeBtn.hide();
55982             this.hide();
55983         }else{
55984             if(!this.isVisible()){
55985                 this.show();
55986             }
55987         }
55988     },
55989
55990     /**
55991      * Adds the passed ContentPanel(s) to this region.
55992      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55993      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
55994      */
55995     add : function(panel){
55996         if(arguments.length > 1){
55997             for(var i = 0, len = arguments.length; i < len; i++) {
55998                 this.add(arguments[i]);
55999             }
56000             return null;
56001         }
56002         if(this.hasPanel(panel)){
56003             this.showPanel(panel);
56004             return panel;
56005         }
56006         panel.setRegion(this);
56007         this.panels.add(panel);
56008         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
56009             this.bodyEl.dom.appendChild(panel.getEl().dom);
56010             if(panel.background !== true){
56011                 this.setActivePanel(panel);
56012             }
56013             this.fireEvent("paneladded", this, panel);
56014             return panel;
56015         }
56016         if(!this.tabs){
56017             this.initTabs();
56018         }else{
56019             this.initPanelAsTab(panel);
56020         }
56021         if(panel.background !== true){
56022             this.tabs.activate(panel.getEl().id);
56023         }
56024         this.fireEvent("paneladded", this, panel);
56025         return panel;
56026     },
56027
56028     /**
56029      * Hides the tab for the specified panel.
56030      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56031      */
56032     hidePanel : function(panel){
56033         if(this.tabs && (panel = this.getPanel(panel))){
56034             this.tabs.hideTab(panel.getEl().id);
56035         }
56036     },
56037
56038     /**
56039      * Unhides the tab for a previously hidden panel.
56040      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56041      */
56042     unhidePanel : function(panel){
56043         if(this.tabs && (panel = this.getPanel(panel))){
56044             this.tabs.unhideTab(panel.getEl().id);
56045         }
56046     },
56047
56048     clearPanels : function(){
56049         while(this.panels.getCount() > 0){
56050              this.remove(this.panels.first());
56051         }
56052     },
56053
56054     /**
56055      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
56056      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56057      * @param {Boolean} preservePanel Overrides the config preservePanel option
56058      * @return {Roo.ContentPanel} The panel that was removed
56059      */
56060     remove : function(panel, preservePanel){
56061         panel = this.getPanel(panel);
56062         if(!panel){
56063             return null;
56064         }
56065         var e = {};
56066         this.fireEvent("beforeremove", this, panel, e);
56067         if(e.cancel === true){
56068             return null;
56069         }
56070         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
56071         var panelId = panel.getId();
56072         this.panels.removeKey(panelId);
56073         if(preservePanel){
56074             document.body.appendChild(panel.getEl().dom);
56075         }
56076         if(this.tabs){
56077             this.tabs.removeTab(panel.getEl().id);
56078         }else if (!preservePanel){
56079             this.bodyEl.dom.removeChild(panel.getEl().dom);
56080         }
56081         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
56082             var p = this.panels.first();
56083             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
56084             tempEl.appendChild(p.getEl().dom);
56085             this.bodyEl.update("");
56086             this.bodyEl.dom.appendChild(p.getEl().dom);
56087             tempEl = null;
56088             this.updateTitle(p.getTitle());
56089             this.tabs = null;
56090             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56091             this.setActivePanel(p);
56092         }
56093         panel.setRegion(null);
56094         if(this.activePanel == panel){
56095             this.activePanel = null;
56096         }
56097         if(this.config.autoDestroy !== false && preservePanel !== true){
56098             try{panel.destroy();}catch(e){}
56099         }
56100         this.fireEvent("panelremoved", this, panel);
56101         return panel;
56102     },
56103
56104     /**
56105      * Returns the TabPanel component used by this region
56106      * @return {Roo.TabPanel}
56107      */
56108     getTabs : function(){
56109         return this.tabs;
56110     },
56111
56112     createTool : function(parentEl, className){
56113         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
56114             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
56115         btn.addClassOnOver("x-layout-tools-button-over");
56116         return btn;
56117     }
56118 });/*
56119  * Based on:
56120  * Ext JS Library 1.1.1
56121  * Copyright(c) 2006-2007, Ext JS, LLC.
56122  *
56123  * Originally Released Under LGPL - original licence link has changed is not relivant.
56124  *
56125  * Fork - LGPL
56126  * <script type="text/javascript">
56127  */
56128  
56129
56130
56131 /**
56132  * @class Roo.SplitLayoutRegion
56133  * @extends Roo.LayoutRegion
56134  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
56135  */
56136 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
56137     this.cursor = cursor;
56138     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
56139 };
56140
56141 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
56142     splitTip : "Drag to resize.",
56143     collapsibleSplitTip : "Drag to resize. Double click to hide.",
56144     useSplitTips : false,
56145
56146     applyConfig : function(config){
56147         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
56148         if(config.split){
56149             if(!this.split){
56150                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
56151                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
56152                 /** The SplitBar for this region 
56153                 * @type Roo.SplitBar */
56154                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
56155                 this.split.on("moved", this.onSplitMove, this);
56156                 this.split.useShim = config.useShim === true;
56157                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
56158                 if(this.useSplitTips){
56159                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
56160                 }
56161                 if(config.collapsible){
56162                     this.split.el.on("dblclick", this.collapse,  this);
56163                 }
56164             }
56165             if(typeof config.minSize != "undefined"){
56166                 this.split.minSize = config.minSize;
56167             }
56168             if(typeof config.maxSize != "undefined"){
56169                 this.split.maxSize = config.maxSize;
56170             }
56171             if(config.hideWhenEmpty || config.hidden || config.collapsed){
56172                 this.hideSplitter();
56173             }
56174         }
56175     },
56176
56177     getHMaxSize : function(){
56178          var cmax = this.config.maxSize || 10000;
56179          var center = this.mgr.getRegion("center");
56180          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
56181     },
56182
56183     getVMaxSize : function(){
56184          var cmax = this.config.maxSize || 10000;
56185          var center = this.mgr.getRegion("center");
56186          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
56187     },
56188
56189     onSplitMove : function(split, newSize){
56190         this.fireEvent("resized", this, newSize);
56191     },
56192     
56193     /** 
56194      * Returns the {@link Roo.SplitBar} for this region.
56195      * @return {Roo.SplitBar}
56196      */
56197     getSplitBar : function(){
56198         return this.split;
56199     },
56200     
56201     hide : function(){
56202         this.hideSplitter();
56203         Roo.SplitLayoutRegion.superclass.hide.call(this);
56204     },
56205
56206     hideSplitter : function(){
56207         if(this.split){
56208             this.split.el.setLocation(-2000,-2000);
56209             this.split.el.hide();
56210         }
56211     },
56212
56213     show : function(){
56214         if(this.split){
56215             this.split.el.show();
56216         }
56217         Roo.SplitLayoutRegion.superclass.show.call(this);
56218     },
56219     
56220     beforeSlide: function(){
56221         if(Roo.isGecko){// firefox overflow auto bug workaround
56222             this.bodyEl.clip();
56223             if(this.tabs) {
56224                 this.tabs.bodyEl.clip();
56225             }
56226             if(this.activePanel){
56227                 this.activePanel.getEl().clip();
56228                 
56229                 if(this.activePanel.beforeSlide){
56230                     this.activePanel.beforeSlide();
56231                 }
56232             }
56233         }
56234     },
56235     
56236     afterSlide : function(){
56237         if(Roo.isGecko){// firefox overflow auto bug workaround
56238             this.bodyEl.unclip();
56239             if(this.tabs) {
56240                 this.tabs.bodyEl.unclip();
56241             }
56242             if(this.activePanel){
56243                 this.activePanel.getEl().unclip();
56244                 if(this.activePanel.afterSlide){
56245                     this.activePanel.afterSlide();
56246                 }
56247             }
56248         }
56249     },
56250
56251     initAutoHide : function(){
56252         if(this.autoHide !== false){
56253             if(!this.autoHideHd){
56254                 var st = new Roo.util.DelayedTask(this.slideIn, this);
56255                 this.autoHideHd = {
56256                     "mouseout": function(e){
56257                         if(!e.within(this.el, true)){
56258                             st.delay(500);
56259                         }
56260                     },
56261                     "mouseover" : function(e){
56262                         st.cancel();
56263                     },
56264                     scope : this
56265                 };
56266             }
56267             this.el.on(this.autoHideHd);
56268         }
56269     },
56270
56271     clearAutoHide : function(){
56272         if(this.autoHide !== false){
56273             this.el.un("mouseout", this.autoHideHd.mouseout);
56274             this.el.un("mouseover", this.autoHideHd.mouseover);
56275         }
56276     },
56277
56278     clearMonitor : function(){
56279         Roo.get(document).un("click", this.slideInIf, this);
56280     },
56281
56282     // these names are backwards but not changed for compat
56283     slideOut : function(){
56284         if(this.isSlid || this.el.hasActiveFx()){
56285             return;
56286         }
56287         this.isSlid = true;
56288         if(this.collapseBtn){
56289             this.collapseBtn.hide();
56290         }
56291         this.closeBtnState = this.closeBtn.getStyle('display');
56292         this.closeBtn.hide();
56293         if(this.stickBtn){
56294             this.stickBtn.show();
56295         }
56296         this.el.show();
56297         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
56298         this.beforeSlide();
56299         this.el.setStyle("z-index", 10001);
56300         this.el.slideIn(this.getSlideAnchor(), {
56301             callback: function(){
56302                 this.afterSlide();
56303                 this.initAutoHide();
56304                 Roo.get(document).on("click", this.slideInIf, this);
56305                 this.fireEvent("slideshow", this);
56306             },
56307             scope: this,
56308             block: true
56309         });
56310     },
56311
56312     afterSlideIn : function(){
56313         this.clearAutoHide();
56314         this.isSlid = false;
56315         this.clearMonitor();
56316         this.el.setStyle("z-index", "");
56317         if(this.collapseBtn){
56318             this.collapseBtn.show();
56319         }
56320         this.closeBtn.setStyle('display', this.closeBtnState);
56321         if(this.stickBtn){
56322             this.stickBtn.hide();
56323         }
56324         this.fireEvent("slidehide", this);
56325     },
56326
56327     slideIn : function(cb){
56328         if(!this.isSlid || this.el.hasActiveFx()){
56329             Roo.callback(cb);
56330             return;
56331         }
56332         this.isSlid = false;
56333         this.beforeSlide();
56334         this.el.slideOut(this.getSlideAnchor(), {
56335             callback: function(){
56336                 this.el.setLeftTop(-10000, -10000);
56337                 this.afterSlide();
56338                 this.afterSlideIn();
56339                 Roo.callback(cb);
56340             },
56341             scope: this,
56342             block: true
56343         });
56344     },
56345     
56346     slideInIf : function(e){
56347         if(!e.within(this.el)){
56348             this.slideIn();
56349         }
56350     },
56351
56352     animateCollapse : function(){
56353         this.beforeSlide();
56354         this.el.setStyle("z-index", 20000);
56355         var anchor = this.getSlideAnchor();
56356         this.el.slideOut(anchor, {
56357             callback : function(){
56358                 this.el.setStyle("z-index", "");
56359                 this.collapsedEl.slideIn(anchor, {duration:.3});
56360                 this.afterSlide();
56361                 this.el.setLocation(-10000,-10000);
56362                 this.el.hide();
56363                 this.fireEvent("collapsed", this);
56364             },
56365             scope: this,
56366             block: true
56367         });
56368     },
56369
56370     animateExpand : function(){
56371         this.beforeSlide();
56372         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
56373         this.el.setStyle("z-index", 20000);
56374         this.collapsedEl.hide({
56375             duration:.1
56376         });
56377         this.el.slideIn(this.getSlideAnchor(), {
56378             callback : function(){
56379                 this.el.setStyle("z-index", "");
56380                 this.afterSlide();
56381                 if(this.split){
56382                     this.split.el.show();
56383                 }
56384                 this.fireEvent("invalidated", this);
56385                 this.fireEvent("expanded", this);
56386             },
56387             scope: this,
56388             block: true
56389         });
56390     },
56391
56392     anchors : {
56393         "west" : "left",
56394         "east" : "right",
56395         "north" : "top",
56396         "south" : "bottom"
56397     },
56398
56399     sanchors : {
56400         "west" : "l",
56401         "east" : "r",
56402         "north" : "t",
56403         "south" : "b"
56404     },
56405
56406     canchors : {
56407         "west" : "tl-tr",
56408         "east" : "tr-tl",
56409         "north" : "tl-bl",
56410         "south" : "bl-tl"
56411     },
56412
56413     getAnchor : function(){
56414         return this.anchors[this.position];
56415     },
56416
56417     getCollapseAnchor : function(){
56418         return this.canchors[this.position];
56419     },
56420
56421     getSlideAnchor : function(){
56422         return this.sanchors[this.position];
56423     },
56424
56425     getAlignAdj : function(){
56426         var cm = this.cmargins;
56427         switch(this.position){
56428             case "west":
56429                 return [0, 0];
56430             break;
56431             case "east":
56432                 return [0, 0];
56433             break;
56434             case "north":
56435                 return [0, 0];
56436             break;
56437             case "south":
56438                 return [0, 0];
56439             break;
56440         }
56441     },
56442
56443     getExpandAdj : function(){
56444         var c = this.collapsedEl, cm = this.cmargins;
56445         switch(this.position){
56446             case "west":
56447                 return [-(cm.right+c.getWidth()+cm.left), 0];
56448             break;
56449             case "east":
56450                 return [cm.right+c.getWidth()+cm.left, 0];
56451             break;
56452             case "north":
56453                 return [0, -(cm.top+cm.bottom+c.getHeight())];
56454             break;
56455             case "south":
56456                 return [0, cm.top+cm.bottom+c.getHeight()];
56457             break;
56458         }
56459     }
56460 });/*
56461  * Based on:
56462  * Ext JS Library 1.1.1
56463  * Copyright(c) 2006-2007, Ext JS, LLC.
56464  *
56465  * Originally Released Under LGPL - original licence link has changed is not relivant.
56466  *
56467  * Fork - LGPL
56468  * <script type="text/javascript">
56469  */
56470 /*
56471  * These classes are private internal classes
56472  */
56473 Roo.CenterLayoutRegion = function(mgr, config){
56474     Roo.LayoutRegion.call(this, mgr, config, "center");
56475     this.visible = true;
56476     this.minWidth = config.minWidth || 20;
56477     this.minHeight = config.minHeight || 20;
56478 };
56479
56480 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
56481     hide : function(){
56482         // center panel can't be hidden
56483     },
56484     
56485     show : function(){
56486         // center panel can't be hidden
56487     },
56488     
56489     getMinWidth: function(){
56490         return this.minWidth;
56491     },
56492     
56493     getMinHeight: function(){
56494         return this.minHeight;
56495     }
56496 });
56497
56498
56499 Roo.NorthLayoutRegion = function(mgr, config){
56500     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
56501     if(this.split){
56502         this.split.placement = Roo.SplitBar.TOP;
56503         this.split.orientation = Roo.SplitBar.VERTICAL;
56504         this.split.el.addClass("x-layout-split-v");
56505     }
56506     var size = config.initialSize || config.height;
56507     if(typeof size != "undefined"){
56508         this.el.setHeight(size);
56509     }
56510 };
56511 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
56512     orientation: Roo.SplitBar.VERTICAL,
56513     getBox : function(){
56514         if(this.collapsed){
56515             return this.collapsedEl.getBox();
56516         }
56517         var box = this.el.getBox();
56518         if(this.split){
56519             box.height += this.split.el.getHeight();
56520         }
56521         return box;
56522     },
56523     
56524     updateBox : function(box){
56525         if(this.split && !this.collapsed){
56526             box.height -= this.split.el.getHeight();
56527             this.split.el.setLeft(box.x);
56528             this.split.el.setTop(box.y+box.height);
56529             this.split.el.setWidth(box.width);
56530         }
56531         if(this.collapsed){
56532             this.updateBody(box.width, null);
56533         }
56534         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56535     }
56536 });
56537
56538 Roo.SouthLayoutRegion = function(mgr, config){
56539     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
56540     if(this.split){
56541         this.split.placement = Roo.SplitBar.BOTTOM;
56542         this.split.orientation = Roo.SplitBar.VERTICAL;
56543         this.split.el.addClass("x-layout-split-v");
56544     }
56545     var size = config.initialSize || config.height;
56546     if(typeof size != "undefined"){
56547         this.el.setHeight(size);
56548     }
56549 };
56550 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
56551     orientation: Roo.SplitBar.VERTICAL,
56552     getBox : function(){
56553         if(this.collapsed){
56554             return this.collapsedEl.getBox();
56555         }
56556         var box = this.el.getBox();
56557         if(this.split){
56558             var sh = this.split.el.getHeight();
56559             box.height += sh;
56560             box.y -= sh;
56561         }
56562         return box;
56563     },
56564     
56565     updateBox : function(box){
56566         if(this.split && !this.collapsed){
56567             var sh = this.split.el.getHeight();
56568             box.height -= sh;
56569             box.y += sh;
56570             this.split.el.setLeft(box.x);
56571             this.split.el.setTop(box.y-sh);
56572             this.split.el.setWidth(box.width);
56573         }
56574         if(this.collapsed){
56575             this.updateBody(box.width, null);
56576         }
56577         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56578     }
56579 });
56580
56581 Roo.EastLayoutRegion = function(mgr, config){
56582     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
56583     if(this.split){
56584         this.split.placement = Roo.SplitBar.RIGHT;
56585         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56586         this.split.el.addClass("x-layout-split-h");
56587     }
56588     var size = config.initialSize || config.width;
56589     if(typeof size != "undefined"){
56590         this.el.setWidth(size);
56591     }
56592 };
56593 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
56594     orientation: Roo.SplitBar.HORIZONTAL,
56595     getBox : function(){
56596         if(this.collapsed){
56597             return this.collapsedEl.getBox();
56598         }
56599         var box = this.el.getBox();
56600         if(this.split){
56601             var sw = this.split.el.getWidth();
56602             box.width += sw;
56603             box.x -= sw;
56604         }
56605         return box;
56606     },
56607
56608     updateBox : function(box){
56609         if(this.split && !this.collapsed){
56610             var sw = this.split.el.getWidth();
56611             box.width -= sw;
56612             this.split.el.setLeft(box.x);
56613             this.split.el.setTop(box.y);
56614             this.split.el.setHeight(box.height);
56615             box.x += sw;
56616         }
56617         if(this.collapsed){
56618             this.updateBody(null, box.height);
56619         }
56620         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56621     }
56622 });
56623
56624 Roo.WestLayoutRegion = function(mgr, config){
56625     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
56626     if(this.split){
56627         this.split.placement = Roo.SplitBar.LEFT;
56628         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56629         this.split.el.addClass("x-layout-split-h");
56630     }
56631     var size = config.initialSize || config.width;
56632     if(typeof size != "undefined"){
56633         this.el.setWidth(size);
56634     }
56635 };
56636 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
56637     orientation: Roo.SplitBar.HORIZONTAL,
56638     getBox : function(){
56639         if(this.collapsed){
56640             return this.collapsedEl.getBox();
56641         }
56642         var box = this.el.getBox();
56643         if(this.split){
56644             box.width += this.split.el.getWidth();
56645         }
56646         return box;
56647     },
56648     
56649     updateBox : function(box){
56650         if(this.split && !this.collapsed){
56651             var sw = this.split.el.getWidth();
56652             box.width -= sw;
56653             this.split.el.setLeft(box.x+box.width);
56654             this.split.el.setTop(box.y);
56655             this.split.el.setHeight(box.height);
56656         }
56657         if(this.collapsed){
56658             this.updateBody(null, box.height);
56659         }
56660         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56661     }
56662 });
56663 /*
56664  * Based on:
56665  * Ext JS Library 1.1.1
56666  * Copyright(c) 2006-2007, Ext JS, LLC.
56667  *
56668  * Originally Released Under LGPL - original licence link has changed is not relivant.
56669  *
56670  * Fork - LGPL
56671  * <script type="text/javascript">
56672  */
56673  
56674  
56675 /*
56676  * Private internal class for reading and applying state
56677  */
56678 Roo.LayoutStateManager = function(layout){
56679      // default empty state
56680      this.state = {
56681         north: {},
56682         south: {},
56683         east: {},
56684         west: {}       
56685     };
56686 };
56687
56688 Roo.LayoutStateManager.prototype = {
56689     init : function(layout, provider){
56690         this.provider = provider;
56691         var state = provider.get(layout.id+"-layout-state");
56692         if(state){
56693             var wasUpdating = layout.isUpdating();
56694             if(!wasUpdating){
56695                 layout.beginUpdate();
56696             }
56697             for(var key in state){
56698                 if(typeof state[key] != "function"){
56699                     var rstate = state[key];
56700                     var r = layout.getRegion(key);
56701                     if(r && rstate){
56702                         if(rstate.size){
56703                             r.resizeTo(rstate.size);
56704                         }
56705                         if(rstate.collapsed == true){
56706                             r.collapse(true);
56707                         }else{
56708                             r.expand(null, true);
56709                         }
56710                     }
56711                 }
56712             }
56713             if(!wasUpdating){
56714                 layout.endUpdate();
56715             }
56716             this.state = state; 
56717         }
56718         this.layout = layout;
56719         layout.on("regionresized", this.onRegionResized, this);
56720         layout.on("regioncollapsed", this.onRegionCollapsed, this);
56721         layout.on("regionexpanded", this.onRegionExpanded, this);
56722     },
56723     
56724     storeState : function(){
56725         this.provider.set(this.layout.id+"-layout-state", this.state);
56726     },
56727     
56728     onRegionResized : function(region, newSize){
56729         this.state[region.getPosition()].size = newSize;
56730         this.storeState();
56731     },
56732     
56733     onRegionCollapsed : function(region){
56734         this.state[region.getPosition()].collapsed = true;
56735         this.storeState();
56736     },
56737     
56738     onRegionExpanded : function(region){
56739         this.state[region.getPosition()].collapsed = false;
56740         this.storeState();
56741     }
56742 };/*
56743  * Based on:
56744  * Ext JS Library 1.1.1
56745  * Copyright(c) 2006-2007, Ext JS, LLC.
56746  *
56747  * Originally Released Under LGPL - original licence link has changed is not relivant.
56748  *
56749  * Fork - LGPL
56750  * <script type="text/javascript">
56751  */
56752 /**
56753  * @class Roo.ContentPanel
56754  * @extends Roo.util.Observable
56755  * @children Roo.form.Form Roo.JsonView Roo.View
56756  * @parent Roo.BorderLayout Roo.LayoutDialog builder
56757  * A basic ContentPanel element.
56758  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
56759  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
56760  * @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
56761  * @cfg {Boolean}   closable      True if the panel can be closed/removed
56762  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
56763  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
56764  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
56765  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
56766  * @cfg {String} title          The title for this panel
56767  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
56768  * @cfg {String} url            Calls {@link #setUrl} with this value
56769  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
56770  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
56771  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
56772  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
56773  * @cfg {String}    style  Extra style to add to the content panel
56774  * @cfg {Roo.menu.Menu} menu  popup menu
56775
56776  * @constructor
56777  * Create a new ContentPanel.
56778  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
56779  * @param {String/Object} config A string to set only the title or a config object
56780  * @param {String} content (optional) Set the HTML content for this panel
56781  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
56782  */
56783 Roo.ContentPanel = function(el, config, content){
56784     
56785      
56786     /*
56787     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
56788         config = el;
56789         el = Roo.id();
56790     }
56791     if (config && config.parentLayout) { 
56792         el = config.parentLayout.el.createChild(); 
56793     }
56794     */
56795     if(el.autoCreate){ // xtype is available if this is called from factory
56796         config = el;
56797         el = Roo.id();
56798     }
56799     this.el = Roo.get(el);
56800     if(!this.el && config && config.autoCreate){
56801         if(typeof config.autoCreate == "object"){
56802             if(!config.autoCreate.id){
56803                 config.autoCreate.id = config.id||el;
56804             }
56805             this.el = Roo.DomHelper.append(document.body,
56806                         config.autoCreate, true);
56807         }else{
56808             this.el = Roo.DomHelper.append(document.body,
56809                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
56810         }
56811     }
56812     
56813     
56814     this.closable = false;
56815     this.loaded = false;
56816     this.active = false;
56817     if(typeof config == "string"){
56818         this.title = config;
56819     }else{
56820         Roo.apply(this, config);
56821     }
56822     
56823     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
56824         this.wrapEl = this.el.wrap();
56825         this.toolbar.container = this.el.insertSibling(false, 'before');
56826         this.toolbar = new Roo.Toolbar(this.toolbar);
56827     }
56828     
56829     // xtype created footer. - not sure if will work as we normally have to render first..
56830     if (this.footer && !this.footer.el && this.footer.xtype) {
56831         if (!this.wrapEl) {
56832             this.wrapEl = this.el.wrap();
56833         }
56834     
56835         this.footer.container = this.wrapEl.createChild();
56836          
56837         this.footer = Roo.factory(this.footer, Roo);
56838         
56839     }
56840     
56841     if(this.resizeEl){
56842         this.resizeEl = Roo.get(this.resizeEl, true);
56843     }else{
56844         this.resizeEl = this.el;
56845     }
56846     // handle view.xtype
56847     
56848  
56849     
56850     
56851     this.addEvents({
56852         /**
56853          * @event activate
56854          * Fires when this panel is activated. 
56855          * @param {Roo.ContentPanel} this
56856          */
56857         "activate" : true,
56858         /**
56859          * @event deactivate
56860          * Fires when this panel is activated. 
56861          * @param {Roo.ContentPanel} this
56862          */
56863         "deactivate" : true,
56864
56865         /**
56866          * @event resize
56867          * Fires when this panel is resized if fitToFrame is true.
56868          * @param {Roo.ContentPanel} this
56869          * @param {Number} width The width after any component adjustments
56870          * @param {Number} height The height after any component adjustments
56871          */
56872         "resize" : true,
56873         
56874          /**
56875          * @event render
56876          * Fires when this tab is created
56877          * @param {Roo.ContentPanel} this
56878          */
56879         "render" : true
56880          
56881         
56882     });
56883     
56884
56885     
56886     
56887     if(this.autoScroll){
56888         this.resizeEl.setStyle("overflow", "auto");
56889     } else {
56890         // fix randome scrolling
56891         this.el.on('scroll', function() {
56892             Roo.log('fix random scolling');
56893             this.scrollTo('top',0); 
56894         });
56895     }
56896     content = content || this.content;
56897     if(content){
56898         this.setContent(content);
56899     }
56900     if(config && config.url){
56901         this.setUrl(this.url, this.params, this.loadOnce);
56902     }
56903     
56904     
56905     
56906     Roo.ContentPanel.superclass.constructor.call(this);
56907     
56908     if (this.view && typeof(this.view.xtype) != 'undefined') {
56909         this.view.el = this.el.appendChild(document.createElement("div"));
56910         this.view = Roo.factory(this.view); 
56911         this.view.render  &&  this.view.render(false, '');  
56912     }
56913     
56914     
56915     this.fireEvent('render', this);
56916 };
56917
56918 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
56919     tabTip:'',
56920     setRegion : function(region){
56921         this.region = region;
56922         if(region){
56923            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
56924         }else{
56925            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
56926         } 
56927     },
56928     
56929     /**
56930      * Returns the toolbar for this Panel if one was configured. 
56931      * @return {Roo.Toolbar} 
56932      */
56933     getToolbar : function(){
56934         return this.toolbar;
56935     },
56936     
56937     setActiveState : function(active){
56938         this.active = active;
56939         if(!active){
56940             this.fireEvent("deactivate", this);
56941         }else{
56942             this.fireEvent("activate", this);
56943         }
56944     },
56945     /**
56946      * Updates this panel's element
56947      * @param {String} content The new content
56948      * @param {Boolean} loadScripts (optional) true to look for and process scripts
56949     */
56950     setContent : function(content, loadScripts){
56951         this.el.update(content, loadScripts);
56952     },
56953
56954     ignoreResize : function(w, h){
56955         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
56956             return true;
56957         }else{
56958             this.lastSize = {width: w, height: h};
56959             return false;
56960         }
56961     },
56962     /**
56963      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
56964      * @return {Roo.UpdateManager} The UpdateManager
56965      */
56966     getUpdateManager : function(){
56967         return this.el.getUpdateManager();
56968     },
56969      /**
56970      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
56971      * @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:
56972 <pre><code>
56973 panel.load({
56974     url: "your-url.php",
56975     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
56976     callback: yourFunction,
56977     scope: yourObject, //(optional scope)
56978     discardUrl: false,
56979     nocache: false,
56980     text: "Loading...",
56981     timeout: 30,
56982     scripts: false
56983 });
56984 </code></pre>
56985      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
56986      * 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.
56987      * @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}
56988      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
56989      * @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.
56990      * @return {Roo.ContentPanel} this
56991      */
56992     load : function(){
56993         var um = this.el.getUpdateManager();
56994         um.update.apply(um, arguments);
56995         return this;
56996     },
56997
56998
56999     /**
57000      * 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.
57001      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
57002      * @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)
57003      * @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)
57004      * @return {Roo.UpdateManager} The UpdateManager
57005      */
57006     setUrl : function(url, params, loadOnce){
57007         if(this.refreshDelegate){
57008             this.removeListener("activate", this.refreshDelegate);
57009         }
57010         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
57011         this.on("activate", this.refreshDelegate);
57012         return this.el.getUpdateManager();
57013     },
57014     
57015     _handleRefresh : function(url, params, loadOnce){
57016         if(!loadOnce || !this.loaded){
57017             var updater = this.el.getUpdateManager();
57018             updater.update(url, params, this._setLoaded.createDelegate(this));
57019         }
57020     },
57021     
57022     _setLoaded : function(){
57023         this.loaded = true;
57024     }, 
57025     
57026     /**
57027      * Returns this panel's id
57028      * @return {String} 
57029      */
57030     getId : function(){
57031         return this.el.id;
57032     },
57033     
57034     /** 
57035      * Returns this panel's element - used by regiosn to add.
57036      * @return {Roo.Element} 
57037      */
57038     getEl : function(){
57039         return this.wrapEl || this.el;
57040     },
57041     
57042     adjustForComponents : function(width, height)
57043     {
57044         //Roo.log('adjustForComponents ');
57045         if(this.resizeEl != this.el){
57046             width -= this.el.getFrameWidth('lr');
57047             height -= this.el.getFrameWidth('tb');
57048         }
57049         if(this.toolbar){
57050             var te = this.toolbar.getEl();
57051             height -= te.getHeight();
57052             te.setWidth(width);
57053         }
57054         if(this.footer){
57055             var te = this.footer.getEl();
57056             //Roo.log("footer:" + te.getHeight());
57057             
57058             height -= te.getHeight();
57059             te.setWidth(width);
57060         }
57061         
57062         
57063         if(this.adjustments){
57064             width += this.adjustments[0];
57065             height += this.adjustments[1];
57066         }
57067         return {"width": width, "height": height};
57068     },
57069     
57070     setSize : function(width, height){
57071         if(this.fitToFrame && !this.ignoreResize(width, height)){
57072             if(this.fitContainer && this.resizeEl != this.el){
57073                 this.el.setSize(width, height);
57074             }
57075             var size = this.adjustForComponents(width, height);
57076             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
57077             this.fireEvent('resize', this, size.width, size.height);
57078         }
57079     },
57080     
57081     /**
57082      * Returns this panel's title
57083      * @return {String} 
57084      */
57085     getTitle : function(){
57086         return this.title;
57087     },
57088     
57089     /**
57090      * Set this panel's title
57091      * @param {String} title
57092      */
57093     setTitle : function(title){
57094         this.title = title;
57095         if(this.region){
57096             this.region.updatePanelTitle(this, title);
57097         }
57098     },
57099     
57100     /**
57101      * Returns true is this panel was configured to be closable
57102      * @return {Boolean} 
57103      */
57104     isClosable : function(){
57105         return this.closable;
57106     },
57107     
57108     beforeSlide : function(){
57109         this.el.clip();
57110         this.resizeEl.clip();
57111     },
57112     
57113     afterSlide : function(){
57114         this.el.unclip();
57115         this.resizeEl.unclip();
57116     },
57117     
57118     /**
57119      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
57120      *   Will fail silently if the {@link #setUrl} method has not been called.
57121      *   This does not activate the panel, just updates its content.
57122      */
57123     refresh : function(){
57124         if(this.refreshDelegate){
57125            this.loaded = false;
57126            this.refreshDelegate();
57127         }
57128     },
57129     
57130     /**
57131      * Destroys this panel
57132      */
57133     destroy : function(){
57134         this.el.removeAllListeners();
57135         var tempEl = document.createElement("span");
57136         tempEl.appendChild(this.el.dom);
57137         tempEl.innerHTML = "";
57138         this.el.remove();
57139         this.el = null;
57140     },
57141     
57142     /**
57143      * form - if the content panel contains a form - this is a reference to it.
57144      * @type {Roo.form.Form}
57145      */
57146     form : false,
57147     /**
57148      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
57149      *    This contains a reference to it.
57150      * @type {Roo.View}
57151      */
57152     view : false,
57153     
57154       /**
57155      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
57156      * <pre><code>
57157
57158 layout.addxtype({
57159        xtype : 'Form',
57160        items: [ .... ]
57161    }
57162 );
57163
57164 </code></pre>
57165      * @param {Object} cfg Xtype definition of item to add.
57166      */
57167     
57168     addxtype : function(cfg) {
57169         // add form..
57170         if (cfg.xtype.match(/^Form$/)) {
57171             
57172             var el;
57173             //if (this.footer) {
57174             //    el = this.footer.container.insertSibling(false, 'before');
57175             //} else {
57176                 el = this.el.createChild();
57177             //}
57178
57179             this.form = new  Roo.form.Form(cfg);
57180             
57181             
57182             if ( this.form.allItems.length) {
57183                 this.form.render(el.dom);
57184             }
57185             return this.form;
57186         }
57187         // should only have one of theses..
57188         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
57189             // views.. should not be just added - used named prop 'view''
57190             
57191             cfg.el = this.el.appendChild(document.createElement("div"));
57192             // factory?
57193             
57194             var ret = new Roo.factory(cfg);
57195              
57196              ret.render && ret.render(false, ''); // render blank..
57197             this.view = ret;
57198             return ret;
57199         }
57200         return false;
57201     }
57202 });
57203
57204 /**
57205  * @class Roo.GridPanel
57206  * @extends Roo.ContentPanel
57207  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57208  * @constructor
57209  * Create a new GridPanel.
57210  * @cfg {Roo.grid.Grid} grid The grid for this panel
57211  */
57212 Roo.GridPanel = function(grid, config){
57213     
57214     // universal ctor...
57215     if (typeof(grid.grid) != 'undefined') {
57216         config = grid;
57217         grid = config.grid;
57218     }
57219     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
57220         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
57221         
57222     this.wrapper.dom.appendChild(grid.getGridEl().dom);
57223     
57224     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
57225     
57226     if(this.toolbar){
57227         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
57228     }
57229     // xtype created footer. - not sure if will work as we normally have to render first..
57230     if (this.footer && !this.footer.el && this.footer.xtype) {
57231         
57232         this.footer.container = this.grid.getView().getFooterPanel(true);
57233         this.footer.dataSource = this.grid.dataSource;
57234         this.footer = Roo.factory(this.footer, Roo);
57235         
57236     }
57237     
57238     grid.monitorWindowResize = false; // turn off autosizing
57239     grid.autoHeight = false;
57240     grid.autoWidth = false;
57241     this.grid = grid;
57242     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
57243 };
57244
57245 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
57246     getId : function(){
57247         return this.grid.id;
57248     },
57249     
57250     /**
57251      * Returns the grid for this panel
57252      * @return {Roo.grid.Grid} 
57253      */
57254     getGrid : function(){
57255         return this.grid;    
57256     },
57257     
57258     setSize : function(width, height){
57259         if(!this.ignoreResize(width, height)){
57260             var grid = this.grid;
57261             var size = this.adjustForComponents(width, height);
57262             grid.getGridEl().setSize(size.width, size.height);
57263             grid.autoSize();
57264         }
57265     },
57266     
57267     beforeSlide : function(){
57268         this.grid.getView().scroller.clip();
57269     },
57270     
57271     afterSlide : function(){
57272         this.grid.getView().scroller.unclip();
57273     },
57274     
57275     destroy : function(){
57276         this.grid.destroy();
57277         delete this.grid;
57278         Roo.GridPanel.superclass.destroy.call(this); 
57279     }
57280 });
57281
57282
57283 /**
57284  * @class Roo.NestedLayoutPanel
57285  * @extends Roo.ContentPanel
57286  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57287  * @cfg Roo.BorderLayout} layout   [required] The layout for this panel
57288  *
57289  * 
57290  * @constructor
57291  * Create a new NestedLayoutPanel.
57292  * 
57293  * 
57294  * @param {Roo.BorderLayout} layout [required] The layout for this panel
57295  * @param {String/Object} config A string to set only the title or a config object
57296  */
57297 Roo.NestedLayoutPanel = function(layout, config)
57298 {
57299     // construct with only one argument..
57300     /* FIXME - implement nicer consturctors
57301     if (layout.layout) {
57302         config = layout;
57303         layout = config.layout;
57304         delete config.layout;
57305     }
57306     if (layout.xtype && !layout.getEl) {
57307         // then layout needs constructing..
57308         layout = Roo.factory(layout, Roo);
57309     }
57310     */
57311     
57312     
57313     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
57314     
57315     layout.monitorWindowResize = false; // turn off autosizing
57316     this.layout = layout;
57317     this.layout.getEl().addClass("x-layout-nested-layout");
57318     
57319     
57320     
57321     
57322 };
57323
57324 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
57325
57326     setSize : function(width, height){
57327         if(!this.ignoreResize(width, height)){
57328             var size = this.adjustForComponents(width, height);
57329             var el = this.layout.getEl();
57330             el.setSize(size.width, size.height);
57331             var touch = el.dom.offsetWidth;
57332             this.layout.layout();
57333             // ie requires a double layout on the first pass
57334             if(Roo.isIE && !this.initialized){
57335                 this.initialized = true;
57336                 this.layout.layout();
57337             }
57338         }
57339     },
57340     
57341     // activate all subpanels if not currently active..
57342     
57343     setActiveState : function(active){
57344         this.active = active;
57345         if(!active){
57346             this.fireEvent("deactivate", this);
57347             return;
57348         }
57349         
57350         this.fireEvent("activate", this);
57351         // not sure if this should happen before or after..
57352         if (!this.layout) {
57353             return; // should not happen..
57354         }
57355         var reg = false;
57356         for (var r in this.layout.regions) {
57357             reg = this.layout.getRegion(r);
57358             if (reg.getActivePanel()) {
57359                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
57360                 reg.setActivePanel(reg.getActivePanel());
57361                 continue;
57362             }
57363             if (!reg.panels.length) {
57364                 continue;
57365             }
57366             reg.showPanel(reg.getPanel(0));
57367         }
57368         
57369         
57370         
57371         
57372     },
57373     
57374     /**
57375      * Returns the nested BorderLayout for this panel
57376      * @return {Roo.BorderLayout} 
57377      */
57378     getLayout : function(){
57379         return this.layout;
57380     },
57381     
57382      /**
57383      * Adds a xtype elements to the layout of the nested panel
57384      * <pre><code>
57385
57386 panel.addxtype({
57387        xtype : 'ContentPanel',
57388        region: 'west',
57389        items: [ .... ]
57390    }
57391 );
57392
57393 panel.addxtype({
57394         xtype : 'NestedLayoutPanel',
57395         region: 'west',
57396         layout: {
57397            center: { },
57398            west: { }   
57399         },
57400         items : [ ... list of content panels or nested layout panels.. ]
57401    }
57402 );
57403 </code></pre>
57404      * @param {Object} cfg Xtype definition of item to add.
57405      */
57406     addxtype : function(cfg) {
57407         return this.layout.addxtype(cfg);
57408     
57409     }
57410 });
57411
57412 Roo.ScrollPanel = function(el, config, content){
57413     config = config || {};
57414     config.fitToFrame = true;
57415     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
57416     
57417     this.el.dom.style.overflow = "hidden";
57418     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
57419     this.el.removeClass("x-layout-inactive-content");
57420     this.el.on("mousewheel", this.onWheel, this);
57421
57422     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
57423     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
57424     up.unselectable(); down.unselectable();
57425     up.on("click", this.scrollUp, this);
57426     down.on("click", this.scrollDown, this);
57427     up.addClassOnOver("x-scroller-btn-over");
57428     down.addClassOnOver("x-scroller-btn-over");
57429     up.addClassOnClick("x-scroller-btn-click");
57430     down.addClassOnClick("x-scroller-btn-click");
57431     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
57432
57433     this.resizeEl = this.el;
57434     this.el = wrap; this.up = up; this.down = down;
57435 };
57436
57437 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
57438     increment : 100,
57439     wheelIncrement : 5,
57440     scrollUp : function(){
57441         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
57442     },
57443
57444     scrollDown : function(){
57445         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
57446     },
57447
57448     afterScroll : function(){
57449         var el = this.resizeEl;
57450         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
57451         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57452         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57453     },
57454
57455     setSize : function(){
57456         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
57457         this.afterScroll();
57458     },
57459
57460     onWheel : function(e){
57461         var d = e.getWheelDelta();
57462         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
57463         this.afterScroll();
57464         e.stopEvent();
57465     },
57466
57467     setContent : function(content, loadScripts){
57468         this.resizeEl.update(content, loadScripts);
57469     }
57470
57471 });
57472
57473
57474
57475 /**
57476  * @class Roo.TreePanel
57477  * @extends Roo.ContentPanel
57478  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57479  * Treepanel component
57480  * 
57481  * @constructor
57482  * Create a new TreePanel. - defaults to fit/scoll contents.
57483  * @param {String/Object} config A string to set only the panel's title, or a config object
57484  */
57485 Roo.TreePanel = function(config){
57486     var el = config.el;
57487     var tree = config.tree;
57488     delete config.tree; 
57489     delete config.el; // hopefull!
57490     
57491     // wrapper for IE7 strict & safari scroll issue
57492     
57493     var treeEl = el.createChild();
57494     config.resizeEl = treeEl;
57495     
57496     
57497     
57498     Roo.TreePanel.superclass.constructor.call(this, el, config);
57499  
57500  
57501     this.tree = new Roo.tree.TreePanel(treeEl , tree);
57502     //console.log(tree);
57503     this.on('activate', function()
57504     {
57505         if (this.tree.rendered) {
57506             return;
57507         }
57508         //console.log('render tree');
57509         this.tree.render();
57510     });
57511     // this should not be needed.. - it's actually the 'el' that resizes?
57512     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
57513     
57514     //this.on('resize',  function (cp, w, h) {
57515     //        this.tree.innerCt.setWidth(w);
57516     //        this.tree.innerCt.setHeight(h);
57517     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
57518     //});
57519
57520         
57521     
57522 };
57523
57524 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
57525     fitToFrame : true,
57526     autoScroll : true,
57527     /*
57528      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
57529      */
57530     tree : false
57531
57532 });
57533
57534
57535
57536
57537
57538
57539
57540
57541
57542
57543
57544 /*
57545  * Based on:
57546  * Ext JS Library 1.1.1
57547  * Copyright(c) 2006-2007, Ext JS, LLC.
57548  *
57549  * Originally Released Under LGPL - original licence link has changed is not relivant.
57550  *
57551  * Fork - LGPL
57552  * <script type="text/javascript">
57553  */
57554  
57555
57556 /**
57557  * @class Roo.ReaderLayout
57558  * @extends Roo.BorderLayout
57559  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
57560  * center region containing two nested regions (a top one for a list view and one for item preview below),
57561  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
57562  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
57563  * expedites the setup of the overall layout and regions for this common application style.
57564  * Example:
57565  <pre><code>
57566 var reader = new Roo.ReaderLayout();
57567 var CP = Roo.ContentPanel;  // shortcut for adding
57568
57569 reader.beginUpdate();
57570 reader.add("north", new CP("north", "North"));
57571 reader.add("west", new CP("west", {title: "West"}));
57572 reader.add("east", new CP("east", {title: "East"}));
57573
57574 reader.regions.listView.add(new CP("listView", "List"));
57575 reader.regions.preview.add(new CP("preview", "Preview"));
57576 reader.endUpdate();
57577 </code></pre>
57578 * @constructor
57579 * Create a new ReaderLayout
57580 * @param {Object} config Configuration options
57581 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
57582 * document.body if omitted)
57583 */
57584 Roo.ReaderLayout = function(config, renderTo){
57585     var c = config || {size:{}};
57586     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
57587         north: c.north !== false ? Roo.apply({
57588             split:false,
57589             initialSize: 32,
57590             titlebar: false
57591         }, c.north) : false,
57592         west: c.west !== false ? Roo.apply({
57593             split:true,
57594             initialSize: 200,
57595             minSize: 175,
57596             maxSize: 400,
57597             titlebar: true,
57598             collapsible: true,
57599             animate: true,
57600             margins:{left:5,right:0,bottom:5,top:5},
57601             cmargins:{left:5,right:5,bottom:5,top:5}
57602         }, c.west) : false,
57603         east: c.east !== false ? Roo.apply({
57604             split:true,
57605             initialSize: 200,
57606             minSize: 175,
57607             maxSize: 400,
57608             titlebar: true,
57609             collapsible: true,
57610             animate: true,
57611             margins:{left:0,right:5,bottom:5,top:5},
57612             cmargins:{left:5,right:5,bottom:5,top:5}
57613         }, c.east) : false,
57614         center: Roo.apply({
57615             tabPosition: 'top',
57616             autoScroll:false,
57617             closeOnTab: true,
57618             titlebar:false,
57619             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
57620         }, c.center)
57621     });
57622
57623     this.el.addClass('x-reader');
57624
57625     this.beginUpdate();
57626
57627     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
57628         south: c.preview !== false ? Roo.apply({
57629             split:true,
57630             initialSize: 200,
57631             minSize: 100,
57632             autoScroll:true,
57633             collapsible:true,
57634             titlebar: true,
57635             cmargins:{top:5,left:0, right:0, bottom:0}
57636         }, c.preview) : false,
57637         center: Roo.apply({
57638             autoScroll:false,
57639             titlebar:false,
57640             minHeight:200
57641         }, c.listView)
57642     });
57643     this.add('center', new Roo.NestedLayoutPanel(inner,
57644             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
57645
57646     this.endUpdate();
57647
57648     this.regions.preview = inner.getRegion('south');
57649     this.regions.listView = inner.getRegion('center');
57650 };
57651
57652 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
57653  * Based on:
57654  * Ext JS Library 1.1.1
57655  * Copyright(c) 2006-2007, Ext JS, LLC.
57656  *
57657  * Originally Released Under LGPL - original licence link has changed is not relivant.
57658  *
57659  * Fork - LGPL
57660  * <script type="text/javascript">
57661  */
57662  
57663 /**
57664  * @class Roo.grid.Grid
57665  * @extends Roo.util.Observable
57666  * This class represents the primary interface of a component based grid control.
57667  * <br><br>Usage:<pre><code>
57668  var grid = new Roo.grid.Grid("my-container-id", {
57669      ds: myDataStore,
57670      cm: myColModel,
57671      selModel: mySelectionModel,
57672      autoSizeColumns: true,
57673      monitorWindowResize: false,
57674      trackMouseOver: true
57675  });
57676  // set any options
57677  grid.render();
57678  * </code></pre>
57679  * <b>Common Problems:</b><br/>
57680  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
57681  * element will correct this<br/>
57682  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
57683  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
57684  * are unpredictable.<br/>
57685  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
57686  * grid to calculate dimensions/offsets.<br/>
57687   * @constructor
57688  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57689  * The container MUST have some type of size defined for the grid to fill. The container will be
57690  * automatically set to position relative if it isn't already.
57691  * @param {Object} config A config object that sets properties on this grid.
57692  */
57693 Roo.grid.Grid = function(container, config){
57694         // initialize the container
57695         this.container = Roo.get(container);
57696         this.container.update("");
57697         this.container.setStyle("overflow", "hidden");
57698     this.container.addClass('x-grid-container');
57699
57700     this.id = this.container.id;
57701
57702     Roo.apply(this, config);
57703     // check and correct shorthanded configs
57704     if(this.ds){
57705         this.dataSource = this.ds;
57706         delete this.ds;
57707     }
57708     if(this.cm){
57709         this.colModel = this.cm;
57710         delete this.cm;
57711     }
57712     if(this.sm){
57713         this.selModel = this.sm;
57714         delete this.sm;
57715     }
57716
57717     if (this.selModel) {
57718         this.selModel = Roo.factory(this.selModel, Roo.grid);
57719         this.sm = this.selModel;
57720         this.sm.xmodule = this.xmodule || false;
57721     }
57722     if (typeof(this.colModel.config) == 'undefined') {
57723         this.colModel = new Roo.grid.ColumnModel(this.colModel);
57724         this.cm = this.colModel;
57725         this.cm.xmodule = this.xmodule || false;
57726     }
57727     if (this.dataSource) {
57728         this.dataSource= Roo.factory(this.dataSource, Roo.data);
57729         this.ds = this.dataSource;
57730         this.ds.xmodule = this.xmodule || false;
57731          
57732     }
57733     
57734     
57735     
57736     if(this.width){
57737         this.container.setWidth(this.width);
57738     }
57739
57740     if(this.height){
57741         this.container.setHeight(this.height);
57742     }
57743     /** @private */
57744         this.addEvents({
57745         // raw events
57746         /**
57747          * @event click
57748          * The raw click event for the entire grid.
57749          * @param {Roo.EventObject} e
57750          */
57751         "click" : true,
57752         /**
57753          * @event dblclick
57754          * The raw dblclick event for the entire grid.
57755          * @param {Roo.EventObject} e
57756          */
57757         "dblclick" : true,
57758         /**
57759          * @event contextmenu
57760          * The raw contextmenu event for the entire grid.
57761          * @param {Roo.EventObject} e
57762          */
57763         "contextmenu" : true,
57764         /**
57765          * @event mousedown
57766          * The raw mousedown event for the entire grid.
57767          * @param {Roo.EventObject} e
57768          */
57769         "mousedown" : true,
57770         /**
57771          * @event mouseup
57772          * The raw mouseup event for the entire grid.
57773          * @param {Roo.EventObject} e
57774          */
57775         "mouseup" : true,
57776         /**
57777          * @event mouseover
57778          * The raw mouseover event for the entire grid.
57779          * @param {Roo.EventObject} e
57780          */
57781         "mouseover" : true,
57782         /**
57783          * @event mouseout
57784          * The raw mouseout event for the entire grid.
57785          * @param {Roo.EventObject} e
57786          */
57787         "mouseout" : true,
57788         /**
57789          * @event keypress
57790          * The raw keypress event for the entire grid.
57791          * @param {Roo.EventObject} e
57792          */
57793         "keypress" : true,
57794         /**
57795          * @event keydown
57796          * The raw keydown event for the entire grid.
57797          * @param {Roo.EventObject} e
57798          */
57799         "keydown" : true,
57800
57801         // custom events
57802
57803         /**
57804          * @event cellclick
57805          * Fires when a cell is clicked
57806          * @param {Grid} this
57807          * @param {Number} rowIndex
57808          * @param {Number} columnIndex
57809          * @param {Roo.EventObject} e
57810          */
57811         "cellclick" : true,
57812         /**
57813          * @event celldblclick
57814          * Fires when a cell is double clicked
57815          * @param {Grid} this
57816          * @param {Number} rowIndex
57817          * @param {Number} columnIndex
57818          * @param {Roo.EventObject} e
57819          */
57820         "celldblclick" : true,
57821         /**
57822          * @event rowclick
57823          * Fires when a row is clicked
57824          * @param {Grid} this
57825          * @param {Number} rowIndex
57826          * @param {Roo.EventObject} e
57827          */
57828         "rowclick" : true,
57829         /**
57830          * @event rowdblclick
57831          * Fires when a row is double clicked
57832          * @param {Grid} this
57833          * @param {Number} rowIndex
57834          * @param {Roo.EventObject} e
57835          */
57836         "rowdblclick" : true,
57837         /**
57838          * @event headerclick
57839          * Fires when a header is clicked
57840          * @param {Grid} this
57841          * @param {Number} columnIndex
57842          * @param {Roo.EventObject} e
57843          */
57844         "headerclick" : true,
57845         /**
57846          * @event headerdblclick
57847          * Fires when a header cell is double clicked
57848          * @param {Grid} this
57849          * @param {Number} columnIndex
57850          * @param {Roo.EventObject} e
57851          */
57852         "headerdblclick" : true,
57853         /**
57854          * @event rowcontextmenu
57855          * Fires when a row is right clicked
57856          * @param {Grid} this
57857          * @param {Number} rowIndex
57858          * @param {Roo.EventObject} e
57859          */
57860         "rowcontextmenu" : true,
57861         /**
57862          * @event cellcontextmenu
57863          * Fires when a cell is right clicked
57864          * @param {Grid} this
57865          * @param {Number} rowIndex
57866          * @param {Number} cellIndex
57867          * @param {Roo.EventObject} e
57868          */
57869          "cellcontextmenu" : true,
57870         /**
57871          * @event headercontextmenu
57872          * Fires when a header is right clicked
57873          * @param {Grid} this
57874          * @param {Number} columnIndex
57875          * @param {Roo.EventObject} e
57876          */
57877         "headercontextmenu" : true,
57878         /**
57879          * @event bodyscroll
57880          * Fires when the body element is scrolled
57881          * @param {Number} scrollLeft
57882          * @param {Number} scrollTop
57883          */
57884         "bodyscroll" : true,
57885         /**
57886          * @event columnresize
57887          * Fires when the user resizes a column
57888          * @param {Number} columnIndex
57889          * @param {Number} newSize
57890          */
57891         "columnresize" : true,
57892         /**
57893          * @event columnmove
57894          * Fires when the user moves a column
57895          * @param {Number} oldIndex
57896          * @param {Number} newIndex
57897          */
57898         "columnmove" : true,
57899         /**
57900          * @event startdrag
57901          * Fires when row(s) start being dragged
57902          * @param {Grid} this
57903          * @param {Roo.GridDD} dd The drag drop object
57904          * @param {event} e The raw browser event
57905          */
57906         "startdrag" : true,
57907         /**
57908          * @event enddrag
57909          * Fires when a drag operation is complete
57910          * @param {Grid} this
57911          * @param {Roo.GridDD} dd The drag drop object
57912          * @param {event} e The raw browser event
57913          */
57914         "enddrag" : true,
57915         /**
57916          * @event dragdrop
57917          * Fires when dragged row(s) are dropped on a valid DD target
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         "dragdrop" : true,
57924         /**
57925          * @event dragover
57926          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57927          * @param {Grid} this
57928          * @param {Roo.GridDD} dd The drag drop object
57929          * @param {String} targetId The target drag drop object
57930          * @param {event} e The raw browser event
57931          */
57932         "dragover" : true,
57933         /**
57934          * @event dragenter
57935          *  Fires when the dragged row(s) first cross another DD target while being dragged
57936          * @param {Grid} this
57937          * @param {Roo.GridDD} dd The drag drop object
57938          * @param {String} targetId The target drag drop object
57939          * @param {event} e The raw browser event
57940          */
57941         "dragenter" : true,
57942         /**
57943          * @event dragout
57944          * Fires when the dragged row(s) leave another DD target while being dragged
57945          * @param {Grid} this
57946          * @param {Roo.GridDD} dd The drag drop object
57947          * @param {String} targetId The target drag drop object
57948          * @param {event} e The raw browser event
57949          */
57950         "dragout" : true,
57951         /**
57952          * @event rowclass
57953          * Fires when a row is rendered, so you can change add a style to it.
57954          * @param {GridView} gridview   The grid view
57955          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57956          */
57957         'rowclass' : true,
57958
57959         /**
57960          * @event render
57961          * Fires when the grid is rendered
57962          * @param {Grid} grid
57963          */
57964         'render' : true
57965     });
57966
57967     Roo.grid.Grid.superclass.constructor.call(this);
57968 };
57969 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
57970     
57971     /**
57972          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
57973          */
57974         /**
57975          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
57976          */
57977         /**
57978          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
57979          */
57980         /**
57981          * @cfg {Roo.grid.Store} ds The data store for the grid
57982          */
57983         /**
57984          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
57985          */
57986         /**
57987      * @cfg {String} ddGroup - drag drop group.
57988      */
57989       /**
57990      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
57991      */
57992
57993     /**
57994      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
57995      */
57996     minColumnWidth : 25,
57997
57998     /**
57999      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
58000      * <b>on initial render.</b> It is more efficient to explicitly size the columns
58001      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
58002      */
58003     autoSizeColumns : false,
58004
58005     /**
58006      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
58007      */
58008     autoSizeHeaders : true,
58009
58010     /**
58011      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
58012      */
58013     monitorWindowResize : true,
58014
58015     /**
58016      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
58017      * rows measured to get a columns size. Default is 0 (all rows).
58018      */
58019     maxRowsToMeasure : 0,
58020
58021     /**
58022      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
58023      */
58024     trackMouseOver : true,
58025
58026     /**
58027     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
58028     */
58029       /**
58030     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
58031     */
58032     
58033     /**
58034     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
58035     */
58036     enableDragDrop : false,
58037     
58038     /**
58039     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
58040     */
58041     enableColumnMove : true,
58042     
58043     /**
58044     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
58045     */
58046     enableColumnHide : true,
58047     
58048     /**
58049     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
58050     */
58051     enableRowHeightSync : false,
58052     
58053     /**
58054     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
58055     */
58056     stripeRows : true,
58057     
58058     /**
58059     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
58060     */
58061     autoHeight : false,
58062
58063     /**
58064      * @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.
58065      */
58066     autoExpandColumn : false,
58067
58068     /**
58069     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
58070     * Default is 50.
58071     */
58072     autoExpandMin : 50,
58073
58074     /**
58075     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
58076     */
58077     autoExpandMax : 1000,
58078
58079     /**
58080     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
58081     */
58082     view : null,
58083
58084     /**
58085     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
58086     */
58087     loadMask : false,
58088     /**
58089     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
58090     */
58091     dropTarget: false,
58092      /**
58093     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
58094     */ 
58095     sortColMenu : false,
58096     
58097     // private
58098     rendered : false,
58099
58100     /**
58101     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
58102     * of a fixed width. Default is false.
58103     */
58104     /**
58105     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
58106     */
58107     
58108     
58109     /**
58110     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
58111     * %0 is replaced with the number of selected rows.
58112     */
58113     ddText : "{0} selected row{1}",
58114     
58115     
58116     /**
58117      * Called once after all setup has been completed and the grid is ready to be rendered.
58118      * @return {Roo.grid.Grid} this
58119      */
58120     render : function()
58121     {
58122         var c = this.container;
58123         // try to detect autoHeight/width mode
58124         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
58125             this.autoHeight = true;
58126         }
58127         var view = this.getView();
58128         view.init(this);
58129
58130         c.on("click", this.onClick, this);
58131         c.on("dblclick", this.onDblClick, this);
58132         c.on("contextmenu", this.onContextMenu, this);
58133         c.on("keydown", this.onKeyDown, this);
58134         if (Roo.isTouch) {
58135             c.on("touchstart", this.onTouchStart, this);
58136         }
58137
58138         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
58139
58140         this.getSelectionModel().init(this);
58141
58142         view.render();
58143
58144         if(this.loadMask){
58145             this.loadMask = new Roo.LoadMask(this.container,
58146                     Roo.apply({store:this.dataSource}, this.loadMask));
58147         }
58148         
58149         
58150         if (this.toolbar && this.toolbar.xtype) {
58151             this.toolbar.container = this.getView().getHeaderPanel(true);
58152             this.toolbar = new Roo.Toolbar(this.toolbar);
58153         }
58154         if (this.footer && this.footer.xtype) {
58155             this.footer.dataSource = this.getDataSource();
58156             this.footer.container = this.getView().getFooterPanel(true);
58157             this.footer = Roo.factory(this.footer, Roo);
58158         }
58159         if (this.dropTarget && this.dropTarget.xtype) {
58160             delete this.dropTarget.xtype;
58161             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
58162         }
58163         
58164         
58165         this.rendered = true;
58166         this.fireEvent('render', this);
58167         return this;
58168     },
58169
58170     /**
58171      * Reconfigures the grid to use a different Store and Column Model.
58172      * The View will be bound to the new objects and refreshed.
58173      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
58174      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
58175      */
58176     reconfigure : function(dataSource, colModel){
58177         if(this.loadMask){
58178             this.loadMask.destroy();
58179             this.loadMask = new Roo.LoadMask(this.container,
58180                     Roo.apply({store:dataSource}, this.loadMask));
58181         }
58182         this.view.bind(dataSource, colModel);
58183         this.dataSource = dataSource;
58184         this.colModel = colModel;
58185         this.view.refresh(true);
58186     },
58187     /**
58188      * addColumns
58189      * Add's a column, default at the end..
58190      
58191      * @param {int} position to add (default end)
58192      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
58193      */
58194     addColumns : function(pos, ar)
58195     {
58196         
58197         for (var i =0;i< ar.length;i++) {
58198             var cfg = ar[i];
58199             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
58200             this.cm.lookup[cfg.id] = cfg;
58201         }
58202         
58203         
58204         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
58205             pos = this.cm.config.length; //this.cm.config.push(cfg);
58206         } 
58207         pos = Math.max(0,pos);
58208         ar.unshift(0);
58209         ar.unshift(pos);
58210         this.cm.config.splice.apply(this.cm.config, ar);
58211         
58212         
58213         
58214         this.view.generateRules(this.cm);
58215         this.view.refresh(true);
58216         
58217     },
58218     
58219     
58220     
58221     
58222     // private
58223     onKeyDown : function(e){
58224         this.fireEvent("keydown", e);
58225     },
58226
58227     /**
58228      * Destroy this grid.
58229      * @param {Boolean} removeEl True to remove the element
58230      */
58231     destroy : function(removeEl, keepListeners){
58232         if(this.loadMask){
58233             this.loadMask.destroy();
58234         }
58235         var c = this.container;
58236         c.removeAllListeners();
58237         this.view.destroy();
58238         this.colModel.purgeListeners();
58239         if(!keepListeners){
58240             this.purgeListeners();
58241         }
58242         c.update("");
58243         if(removeEl === true){
58244             c.remove();
58245         }
58246     },
58247
58248     // private
58249     processEvent : function(name, e){
58250         // does this fire select???
58251         //Roo.log('grid:processEvent '  + name);
58252         
58253         if (name != 'touchstart' ) {
58254             this.fireEvent(name, e);    
58255         }
58256         
58257         var t = e.getTarget();
58258         var v = this.view;
58259         var header = v.findHeaderIndex(t);
58260         if(header !== false){
58261             var ename = name == 'touchstart' ? 'click' : name;
58262              
58263             this.fireEvent("header" + ename, this, header, e);
58264         }else{
58265             var row = v.findRowIndex(t);
58266             var cell = v.findCellIndex(t);
58267             if (name == 'touchstart') {
58268                 // first touch is always a click.
58269                 // hopefull this happens after selection is updated.?
58270                 name = false;
58271                 
58272                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
58273                     var cs = this.selModel.getSelectedCell();
58274                     if (row == cs[0] && cell == cs[1]){
58275                         name = 'dblclick';
58276                     }
58277                 }
58278                 if (typeof(this.selModel.getSelections) != 'undefined') {
58279                     var cs = this.selModel.getSelections();
58280                     var ds = this.dataSource;
58281                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
58282                         name = 'dblclick';
58283                     }
58284                 }
58285                 if (!name) {
58286                     return;
58287                 }
58288             }
58289             
58290             
58291             if(row !== false){
58292                 this.fireEvent("row" + name, this, row, e);
58293                 if(cell !== false){
58294                     this.fireEvent("cell" + name, this, row, cell, e);
58295                 }
58296             }
58297         }
58298     },
58299
58300     // private
58301     onClick : function(e){
58302         this.processEvent("click", e);
58303     },
58304    // private
58305     onTouchStart : function(e){
58306         this.processEvent("touchstart", e);
58307     },
58308
58309     // private
58310     onContextMenu : function(e, t){
58311         this.processEvent("contextmenu", e);
58312     },
58313
58314     // private
58315     onDblClick : function(e){
58316         this.processEvent("dblclick", e);
58317     },
58318
58319     // private
58320     walkCells : function(row, col, step, fn, scope){
58321         var cm = this.colModel, clen = cm.getColumnCount();
58322         var ds = this.dataSource, rlen = ds.getCount(), first = true;
58323         if(step < 0){
58324             if(col < 0){
58325                 row--;
58326                 first = false;
58327             }
58328             while(row >= 0){
58329                 if(!first){
58330                     col = clen-1;
58331                 }
58332                 first = false;
58333                 while(col >= 0){
58334                     if(fn.call(scope || this, row, col, cm) === true){
58335                         return [row, col];
58336                     }
58337                     col--;
58338                 }
58339                 row--;
58340             }
58341         } else {
58342             if(col >= clen){
58343                 row++;
58344                 first = false;
58345             }
58346             while(row < rlen){
58347                 if(!first){
58348                     col = 0;
58349                 }
58350                 first = false;
58351                 while(col < clen){
58352                     if(fn.call(scope || this, row, col, cm) === true){
58353                         return [row, col];
58354                     }
58355                     col++;
58356                 }
58357                 row++;
58358             }
58359         }
58360         return null;
58361     },
58362
58363     // private
58364     getSelections : function(){
58365         return this.selModel.getSelections();
58366     },
58367
58368     /**
58369      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
58370      * but if manual update is required this method will initiate it.
58371      */
58372     autoSize : function(){
58373         if(this.rendered){
58374             this.view.layout();
58375             if(this.view.adjustForScroll){
58376                 this.view.adjustForScroll();
58377             }
58378         }
58379     },
58380
58381     /**
58382      * Returns the grid's underlying element.
58383      * @return {Element} The element
58384      */
58385     getGridEl : function(){
58386         return this.container;
58387     },
58388
58389     // private for compatibility, overridden by editor grid
58390     stopEditing : function(){},
58391
58392     /**
58393      * Returns the grid's SelectionModel.
58394      * @return {SelectionModel}
58395      */
58396     getSelectionModel : function(){
58397         if(!this.selModel){
58398             this.selModel = new Roo.grid.RowSelectionModel();
58399         }
58400         return this.selModel;
58401     },
58402
58403     /**
58404      * Returns the grid's DataSource.
58405      * @return {DataSource}
58406      */
58407     getDataSource : function(){
58408         return this.dataSource;
58409     },
58410
58411     /**
58412      * Returns the grid's ColumnModel.
58413      * @return {ColumnModel}
58414      */
58415     getColumnModel : function(){
58416         return this.colModel;
58417     },
58418
58419     /**
58420      * Returns the grid's GridView object.
58421      * @return {GridView}
58422      */
58423     getView : function(){
58424         if(!this.view){
58425             this.view = new Roo.grid.GridView(this.viewConfig);
58426             this.relayEvents(this.view, [
58427                 "beforerowremoved", "beforerowsinserted",
58428                 "beforerefresh", "rowremoved",
58429                 "rowsinserted", "rowupdated" ,"refresh"
58430             ]);
58431         }
58432         return this.view;
58433     },
58434     /**
58435      * Called to get grid's drag proxy text, by default returns this.ddText.
58436      * Override this to put something different in the dragged text.
58437      * @return {String}
58438      */
58439     getDragDropText : function(){
58440         var count = this.selModel.getCount();
58441         return String.format(this.ddText, count, count == 1 ? '' : 's');
58442     }
58443 });
58444 /*
58445  * Based on:
58446  * Ext JS Library 1.1.1
58447  * Copyright(c) 2006-2007, Ext JS, LLC.
58448  *
58449  * Originally Released Under LGPL - original licence link has changed is not relivant.
58450  *
58451  * Fork - LGPL
58452  * <script type="text/javascript">
58453  */
58454  /**
58455  * @class Roo.grid.AbstractGridView
58456  * @extends Roo.util.Observable
58457  * @abstract
58458  * Abstract base class for grid Views
58459  * @constructor
58460  */
58461 Roo.grid.AbstractGridView = function(){
58462         this.grid = null;
58463         
58464         this.events = {
58465             "beforerowremoved" : true,
58466             "beforerowsinserted" : true,
58467             "beforerefresh" : true,
58468             "rowremoved" : true,
58469             "rowsinserted" : true,
58470             "rowupdated" : true,
58471             "refresh" : true
58472         };
58473     Roo.grid.AbstractGridView.superclass.constructor.call(this);
58474 };
58475
58476 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
58477     rowClass : "x-grid-row",
58478     cellClass : "x-grid-cell",
58479     tdClass : "x-grid-td",
58480     hdClass : "x-grid-hd",
58481     splitClass : "x-grid-hd-split",
58482     
58483     init: function(grid){
58484         this.grid = grid;
58485                 var cid = this.grid.getGridEl().id;
58486         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
58487         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
58488         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
58489         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
58490         },
58491         
58492     getColumnRenderers : function(){
58493         var renderers = [];
58494         var cm = this.grid.colModel;
58495         var colCount = cm.getColumnCount();
58496         for(var i = 0; i < colCount; i++){
58497             renderers[i] = cm.getRenderer(i);
58498         }
58499         return renderers;
58500     },
58501     
58502     getColumnIds : function(){
58503         var ids = [];
58504         var cm = this.grid.colModel;
58505         var colCount = cm.getColumnCount();
58506         for(var i = 0; i < colCount; i++){
58507             ids[i] = cm.getColumnId(i);
58508         }
58509         return ids;
58510     },
58511     
58512     getDataIndexes : function(){
58513         if(!this.indexMap){
58514             this.indexMap = this.buildIndexMap();
58515         }
58516         return this.indexMap.colToData;
58517     },
58518     
58519     getColumnIndexByDataIndex : function(dataIndex){
58520         if(!this.indexMap){
58521             this.indexMap = this.buildIndexMap();
58522         }
58523         return this.indexMap.dataToCol[dataIndex];
58524     },
58525     
58526     /**
58527      * Set a css style for a column dynamically. 
58528      * @param {Number} colIndex The index of the column
58529      * @param {String} name The css property name
58530      * @param {String} value The css value
58531      */
58532     setCSSStyle : function(colIndex, name, value){
58533         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
58534         Roo.util.CSS.updateRule(selector, name, value);
58535     },
58536     
58537     generateRules : function(cm){
58538         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
58539         Roo.util.CSS.removeStyleSheet(rulesId);
58540         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58541             var cid = cm.getColumnId(i);
58542             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
58543                          this.tdSelector, cid, " {\n}\n",
58544                          this.hdSelector, cid, " {\n}\n",
58545                          this.splitSelector, cid, " {\n}\n");
58546         }
58547         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58548     }
58549 });/*
58550  * Based on:
58551  * Ext JS Library 1.1.1
58552  * Copyright(c) 2006-2007, Ext JS, LLC.
58553  *
58554  * Originally Released Under LGPL - original licence link has changed is not relivant.
58555  *
58556  * Fork - LGPL
58557  * <script type="text/javascript">
58558  */
58559
58560 // private
58561 // This is a support class used internally by the Grid components
58562 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
58563     this.grid = grid;
58564     this.view = grid.getView();
58565     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58566     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
58567     if(hd2){
58568         this.setHandleElId(Roo.id(hd));
58569         this.setOuterHandleElId(Roo.id(hd2));
58570     }
58571     this.scroll = false;
58572 };
58573 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
58574     maxDragWidth: 120,
58575     getDragData : function(e){
58576         var t = Roo.lib.Event.getTarget(e);
58577         var h = this.view.findHeaderCell(t);
58578         if(h){
58579             return {ddel: h.firstChild, header:h};
58580         }
58581         return false;
58582     },
58583
58584     onInitDrag : function(e){
58585         this.view.headersDisabled = true;
58586         var clone = this.dragData.ddel.cloneNode(true);
58587         clone.id = Roo.id();
58588         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
58589         this.proxy.update(clone);
58590         return true;
58591     },
58592
58593     afterValidDrop : function(){
58594         var v = this.view;
58595         setTimeout(function(){
58596             v.headersDisabled = false;
58597         }, 50);
58598     },
58599
58600     afterInvalidDrop : function(){
58601         var v = this.view;
58602         setTimeout(function(){
58603             v.headersDisabled = false;
58604         }, 50);
58605     }
58606 });
58607 /*
58608  * Based on:
58609  * Ext JS Library 1.1.1
58610  * Copyright(c) 2006-2007, Ext JS, LLC.
58611  *
58612  * Originally Released Under LGPL - original licence link has changed is not relivant.
58613  *
58614  * Fork - LGPL
58615  * <script type="text/javascript">
58616  */
58617 // private
58618 // This is a support class used internally by the Grid components
58619 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
58620     this.grid = grid;
58621     this.view = grid.getView();
58622     // split the proxies so they don't interfere with mouse events
58623     this.proxyTop = Roo.DomHelper.append(document.body, {
58624         cls:"col-move-top", html:"&#160;"
58625     }, true);
58626     this.proxyBottom = Roo.DomHelper.append(document.body, {
58627         cls:"col-move-bottom", html:"&#160;"
58628     }, true);
58629     this.proxyTop.hide = this.proxyBottom.hide = function(){
58630         this.setLeftTop(-100,-100);
58631         this.setStyle("visibility", "hidden");
58632     };
58633     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58634     // temporarily disabled
58635     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
58636     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
58637 };
58638 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
58639     proxyOffsets : [-4, -9],
58640     fly: Roo.Element.fly,
58641
58642     getTargetFromEvent : function(e){
58643         var t = Roo.lib.Event.getTarget(e);
58644         var cindex = this.view.findCellIndex(t);
58645         if(cindex !== false){
58646             return this.view.getHeaderCell(cindex);
58647         }
58648         return null;
58649     },
58650
58651     nextVisible : function(h){
58652         var v = this.view, cm = this.grid.colModel;
58653         h = h.nextSibling;
58654         while(h){
58655             if(!cm.isHidden(v.getCellIndex(h))){
58656                 return h;
58657             }
58658             h = h.nextSibling;
58659         }
58660         return null;
58661     },
58662
58663     prevVisible : function(h){
58664         var v = this.view, cm = this.grid.colModel;
58665         h = h.prevSibling;
58666         while(h){
58667             if(!cm.isHidden(v.getCellIndex(h))){
58668                 return h;
58669             }
58670             h = h.prevSibling;
58671         }
58672         return null;
58673     },
58674
58675     positionIndicator : function(h, n, e){
58676         var x = Roo.lib.Event.getPageX(e);
58677         var r = Roo.lib.Dom.getRegion(n.firstChild);
58678         var px, pt, py = r.top + this.proxyOffsets[1];
58679         if((r.right - x) <= (r.right-r.left)/2){
58680             px = r.right+this.view.borderWidth;
58681             pt = "after";
58682         }else{
58683             px = r.left;
58684             pt = "before";
58685         }
58686         var oldIndex = this.view.getCellIndex(h);
58687         var newIndex = this.view.getCellIndex(n);
58688
58689         if(this.grid.colModel.isFixed(newIndex)){
58690             return false;
58691         }
58692
58693         var locked = this.grid.colModel.isLocked(newIndex);
58694
58695         if(pt == "after"){
58696             newIndex++;
58697         }
58698         if(oldIndex < newIndex){
58699             newIndex--;
58700         }
58701         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
58702             return false;
58703         }
58704         px +=  this.proxyOffsets[0];
58705         this.proxyTop.setLeftTop(px, py);
58706         this.proxyTop.show();
58707         if(!this.bottomOffset){
58708             this.bottomOffset = this.view.mainHd.getHeight();
58709         }
58710         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
58711         this.proxyBottom.show();
58712         return pt;
58713     },
58714
58715     onNodeEnter : function(n, dd, e, data){
58716         if(data.header != n){
58717             this.positionIndicator(data.header, n, e);
58718         }
58719     },
58720
58721     onNodeOver : function(n, dd, e, data){
58722         var result = false;
58723         if(data.header != n){
58724             result = this.positionIndicator(data.header, n, e);
58725         }
58726         if(!result){
58727             this.proxyTop.hide();
58728             this.proxyBottom.hide();
58729         }
58730         return result ? this.dropAllowed : this.dropNotAllowed;
58731     },
58732
58733     onNodeOut : function(n, dd, e, data){
58734         this.proxyTop.hide();
58735         this.proxyBottom.hide();
58736     },
58737
58738     onNodeDrop : function(n, dd, e, data){
58739         var h = data.header;
58740         if(h != n){
58741             var cm = this.grid.colModel;
58742             var x = Roo.lib.Event.getPageX(e);
58743             var r = Roo.lib.Dom.getRegion(n.firstChild);
58744             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
58745             var oldIndex = this.view.getCellIndex(h);
58746             var newIndex = this.view.getCellIndex(n);
58747             var locked = cm.isLocked(newIndex);
58748             if(pt == "after"){
58749                 newIndex++;
58750             }
58751             if(oldIndex < newIndex){
58752                 newIndex--;
58753             }
58754             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
58755                 return false;
58756             }
58757             cm.setLocked(oldIndex, locked, true);
58758             cm.moveColumn(oldIndex, newIndex);
58759             this.grid.fireEvent("columnmove", oldIndex, newIndex);
58760             return true;
58761         }
58762         return false;
58763     }
58764 });
58765 /*
58766  * Based on:
58767  * Ext JS Library 1.1.1
58768  * Copyright(c) 2006-2007, Ext JS, LLC.
58769  *
58770  * Originally Released Under LGPL - original licence link has changed is not relivant.
58771  *
58772  * Fork - LGPL
58773  * <script type="text/javascript">
58774  */
58775   
58776 /**
58777  * @class Roo.grid.GridView
58778  * @extends Roo.util.Observable
58779  *
58780  * @constructor
58781  * @param {Object} config
58782  */
58783 Roo.grid.GridView = function(config){
58784     Roo.grid.GridView.superclass.constructor.call(this);
58785     this.el = null;
58786
58787     Roo.apply(this, config);
58788 };
58789
58790 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
58791
58792     unselectable :  'unselectable="on"',
58793     unselectableCls :  'x-unselectable',
58794     
58795     
58796     rowClass : "x-grid-row",
58797
58798     cellClass : "x-grid-col",
58799
58800     tdClass : "x-grid-td",
58801
58802     hdClass : "x-grid-hd",
58803
58804     splitClass : "x-grid-split",
58805
58806     sortClasses : ["sort-asc", "sort-desc"],
58807
58808     enableMoveAnim : false,
58809
58810     hlColor: "C3DAF9",
58811
58812     dh : Roo.DomHelper,
58813
58814     fly : Roo.Element.fly,
58815
58816     css : Roo.util.CSS,
58817
58818     borderWidth: 1,
58819
58820     splitOffset: 3,
58821
58822     scrollIncrement : 22,
58823
58824     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
58825
58826     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
58827
58828     bind : function(ds, cm){
58829         if(this.ds){
58830             this.ds.un("load", this.onLoad, this);
58831             this.ds.un("datachanged", this.onDataChange, this);
58832             this.ds.un("add", this.onAdd, this);
58833             this.ds.un("remove", this.onRemove, this);
58834             this.ds.un("update", this.onUpdate, this);
58835             this.ds.un("clear", this.onClear, this);
58836         }
58837         if(ds){
58838             ds.on("load", this.onLoad, this);
58839             ds.on("datachanged", this.onDataChange, this);
58840             ds.on("add", this.onAdd, this);
58841             ds.on("remove", this.onRemove, this);
58842             ds.on("update", this.onUpdate, this);
58843             ds.on("clear", this.onClear, this);
58844         }
58845         this.ds = ds;
58846
58847         if(this.cm){
58848             this.cm.un("widthchange", this.onColWidthChange, this);
58849             this.cm.un("headerchange", this.onHeaderChange, this);
58850             this.cm.un("hiddenchange", this.onHiddenChange, this);
58851             this.cm.un("columnmoved", this.onColumnMove, this);
58852             this.cm.un("columnlockchange", this.onColumnLock, this);
58853         }
58854         if(cm){
58855             this.generateRules(cm);
58856             cm.on("widthchange", this.onColWidthChange, this);
58857             cm.on("headerchange", this.onHeaderChange, this);
58858             cm.on("hiddenchange", this.onHiddenChange, this);
58859             cm.on("columnmoved", this.onColumnMove, this);
58860             cm.on("columnlockchange", this.onColumnLock, this);
58861         }
58862         this.cm = cm;
58863     },
58864
58865     init: function(grid){
58866         Roo.grid.GridView.superclass.init.call(this, grid);
58867
58868         this.bind(grid.dataSource, grid.colModel);
58869
58870         grid.on("headerclick", this.handleHeaderClick, this);
58871
58872         if(grid.trackMouseOver){
58873             grid.on("mouseover", this.onRowOver, this);
58874             grid.on("mouseout", this.onRowOut, this);
58875         }
58876         grid.cancelTextSelection = function(){};
58877         this.gridId = grid.id;
58878
58879         var tpls = this.templates || {};
58880
58881         if(!tpls.master){
58882             tpls.master = new Roo.Template(
58883                '<div class="x-grid" hidefocus="true">',
58884                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
58885                   '<div class="x-grid-topbar"></div>',
58886                   '<div class="x-grid-scroller"><div></div></div>',
58887                   '<div class="x-grid-locked">',
58888                       '<div class="x-grid-header">{lockedHeader}</div>',
58889                       '<div class="x-grid-body">{lockedBody}</div>',
58890                   "</div>",
58891                   '<div class="x-grid-viewport">',
58892                       '<div class="x-grid-header">{header}</div>',
58893                       '<div class="x-grid-body">{body}</div>',
58894                   "</div>",
58895                   '<div class="x-grid-bottombar"></div>',
58896                  
58897                   '<div class="x-grid-resize-proxy">&#160;</div>',
58898                "</div>"
58899             );
58900             tpls.master.disableformats = true;
58901         }
58902
58903         if(!tpls.header){
58904             tpls.header = new Roo.Template(
58905                '<table border="0" cellspacing="0" cellpadding="0">',
58906                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
58907                "</table>{splits}"
58908             );
58909             tpls.header.disableformats = true;
58910         }
58911         tpls.header.compile();
58912
58913         if(!tpls.hcell){
58914             tpls.hcell = new Roo.Template(
58915                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
58916                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
58917                 "</div></td>"
58918              );
58919              tpls.hcell.disableFormats = true;
58920         }
58921         tpls.hcell.compile();
58922
58923         if(!tpls.hsplit){
58924             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
58925                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
58926             tpls.hsplit.disableFormats = true;
58927         }
58928         tpls.hsplit.compile();
58929
58930         if(!tpls.body){
58931             tpls.body = new Roo.Template(
58932                '<table border="0" cellspacing="0" cellpadding="0">',
58933                "<tbody>{rows}</tbody>",
58934                "</table>"
58935             );
58936             tpls.body.disableFormats = true;
58937         }
58938         tpls.body.compile();
58939
58940         if(!tpls.row){
58941             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
58942             tpls.row.disableFormats = true;
58943         }
58944         tpls.row.compile();
58945
58946         if(!tpls.cell){
58947             tpls.cell = new Roo.Template(
58948                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
58949                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
58950                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
58951                 "</td>"
58952             );
58953             tpls.cell.disableFormats = true;
58954         }
58955         tpls.cell.compile();
58956
58957         this.templates = tpls;
58958     },
58959
58960     // remap these for backwards compat
58961     onColWidthChange : function(){
58962         this.updateColumns.apply(this, arguments);
58963     },
58964     onHeaderChange : function(){
58965         this.updateHeaders.apply(this, arguments);
58966     }, 
58967     onHiddenChange : function(){
58968         this.handleHiddenChange.apply(this, arguments);
58969     },
58970     onColumnMove : function(){
58971         this.handleColumnMove.apply(this, arguments);
58972     },
58973     onColumnLock : function(){
58974         this.handleLockChange.apply(this, arguments);
58975     },
58976
58977     onDataChange : function(){
58978         this.refresh();
58979         this.updateHeaderSortState();
58980     },
58981
58982     onClear : function(){
58983         this.refresh();
58984     },
58985
58986     onUpdate : function(ds, record){
58987         this.refreshRow(record);
58988     },
58989
58990     refreshRow : function(record){
58991         var ds = this.ds, index;
58992         if(typeof record == 'number'){
58993             index = record;
58994             record = ds.getAt(index);
58995         }else{
58996             index = ds.indexOf(record);
58997         }
58998         this.insertRows(ds, index, index, true);
58999         this.onRemove(ds, record, index+1, true);
59000         this.syncRowHeights(index, index);
59001         this.layout();
59002         this.fireEvent("rowupdated", this, index, record);
59003     },
59004
59005     onAdd : function(ds, records, index){
59006         this.insertRows(ds, index, index + (records.length-1));
59007     },
59008
59009     onRemove : function(ds, record, index, isUpdate){
59010         if(isUpdate !== true){
59011             this.fireEvent("beforerowremoved", this, index, record);
59012         }
59013         var bt = this.getBodyTable(), lt = this.getLockedTable();
59014         if(bt.rows[index]){
59015             bt.firstChild.removeChild(bt.rows[index]);
59016         }
59017         if(lt.rows[index]){
59018             lt.firstChild.removeChild(lt.rows[index]);
59019         }
59020         if(isUpdate !== true){
59021             this.stripeRows(index);
59022             this.syncRowHeights(index, index);
59023             this.layout();
59024             this.fireEvent("rowremoved", this, index, record);
59025         }
59026     },
59027
59028     onLoad : function(){
59029         this.scrollToTop();
59030     },
59031
59032     /**
59033      * Scrolls the grid to the top
59034      */
59035     scrollToTop : function(){
59036         if(this.scroller){
59037             this.scroller.dom.scrollTop = 0;
59038             this.syncScroll();
59039         }
59040     },
59041
59042     /**
59043      * Gets a panel in the header of the grid that can be used for toolbars etc.
59044      * After modifying the contents of this panel a call to grid.autoSize() may be
59045      * required to register any changes in size.
59046      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
59047      * @return Roo.Element
59048      */
59049     getHeaderPanel : function(doShow){
59050         if(doShow){
59051             this.headerPanel.show();
59052         }
59053         return this.headerPanel;
59054     },
59055
59056     /**
59057      * Gets a panel in the footer of the grid that can be used for toolbars etc.
59058      * After modifying the contents of this panel a call to grid.autoSize() may be
59059      * required to register any changes in size.
59060      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
59061      * @return Roo.Element
59062      */
59063     getFooterPanel : function(doShow){
59064         if(doShow){
59065             this.footerPanel.show();
59066         }
59067         return this.footerPanel;
59068     },
59069
59070     initElements : function(){
59071         var E = Roo.Element;
59072         var el = this.grid.getGridEl().dom.firstChild;
59073         var cs = el.childNodes;
59074
59075         this.el = new E(el);
59076         
59077          this.focusEl = new E(el.firstChild);
59078         this.focusEl.swallowEvent("click", true);
59079         
59080         this.headerPanel = new E(cs[1]);
59081         this.headerPanel.enableDisplayMode("block");
59082
59083         this.scroller = new E(cs[2]);
59084         this.scrollSizer = new E(this.scroller.dom.firstChild);
59085
59086         this.lockedWrap = new E(cs[3]);
59087         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
59088         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
59089
59090         this.mainWrap = new E(cs[4]);
59091         this.mainHd = new E(this.mainWrap.dom.firstChild);
59092         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
59093
59094         this.footerPanel = new E(cs[5]);
59095         this.footerPanel.enableDisplayMode("block");
59096
59097         this.resizeProxy = new E(cs[6]);
59098
59099         this.headerSelector = String.format(
59100            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
59101            this.lockedHd.id, this.mainHd.id
59102         );
59103
59104         this.splitterSelector = String.format(
59105            '#{0} div.x-grid-split, #{1} div.x-grid-split',
59106            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
59107         );
59108     },
59109     idToCssName : function(s)
59110     {
59111         return s.replace(/[^a-z0-9]+/ig, '-');
59112     },
59113
59114     getHeaderCell : function(index){
59115         return Roo.DomQuery.select(this.headerSelector)[index];
59116     },
59117
59118     getHeaderCellMeasure : function(index){
59119         return this.getHeaderCell(index).firstChild;
59120     },
59121
59122     getHeaderCellText : function(index){
59123         return this.getHeaderCell(index).firstChild.firstChild;
59124     },
59125
59126     getLockedTable : function(){
59127         return this.lockedBody.dom.firstChild;
59128     },
59129
59130     getBodyTable : function(){
59131         return this.mainBody.dom.firstChild;
59132     },
59133
59134     getLockedRow : function(index){
59135         return this.getLockedTable().rows[index];
59136     },
59137
59138     getRow : function(index){
59139         return this.getBodyTable().rows[index];
59140     },
59141
59142     getRowComposite : function(index){
59143         if(!this.rowEl){
59144             this.rowEl = new Roo.CompositeElementLite();
59145         }
59146         var els = [], lrow, mrow;
59147         if(lrow = this.getLockedRow(index)){
59148             els.push(lrow);
59149         }
59150         if(mrow = this.getRow(index)){
59151             els.push(mrow);
59152         }
59153         this.rowEl.elements = els;
59154         return this.rowEl;
59155     },
59156     /**
59157      * Gets the 'td' of the cell
59158      * 
59159      * @param {Integer} rowIndex row to select
59160      * @param {Integer} colIndex column to select
59161      * 
59162      * @return {Object} 
59163      */
59164     getCell : function(rowIndex, colIndex){
59165         var locked = this.cm.getLockedCount();
59166         var source;
59167         if(colIndex < locked){
59168             source = this.lockedBody.dom.firstChild;
59169         }else{
59170             source = this.mainBody.dom.firstChild;
59171             colIndex -= locked;
59172         }
59173         return source.rows[rowIndex].childNodes[colIndex];
59174     },
59175
59176     getCellText : function(rowIndex, colIndex){
59177         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
59178     },
59179
59180     getCellBox : function(cell){
59181         var b = this.fly(cell).getBox();
59182         if(Roo.isOpera){ // opera fails to report the Y
59183             b.y = cell.offsetTop + this.mainBody.getY();
59184         }
59185         return b;
59186     },
59187
59188     getCellIndex : function(cell){
59189         var id = String(cell.className).match(this.cellRE);
59190         if(id){
59191             return parseInt(id[1], 10);
59192         }
59193         return 0;
59194     },
59195
59196     findHeaderIndex : function(n){
59197         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59198         return r ? this.getCellIndex(r) : false;
59199     },
59200
59201     findHeaderCell : function(n){
59202         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59203         return r ? r : false;
59204     },
59205
59206     findRowIndex : function(n){
59207         if(!n){
59208             return false;
59209         }
59210         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
59211         return r ? r.rowIndex : false;
59212     },
59213
59214     findCellIndex : function(node){
59215         var stop = this.el.dom;
59216         while(node && node != stop){
59217             if(this.findRE.test(node.className)){
59218                 return this.getCellIndex(node);
59219             }
59220             node = node.parentNode;
59221         }
59222         return false;
59223     },
59224
59225     getColumnId : function(index){
59226         return this.cm.getColumnId(index);
59227     },
59228
59229     getSplitters : function()
59230     {
59231         if(this.splitterSelector){
59232            return Roo.DomQuery.select(this.splitterSelector);
59233         }else{
59234             return null;
59235       }
59236     },
59237
59238     getSplitter : function(index){
59239         return this.getSplitters()[index];
59240     },
59241
59242     onRowOver : function(e, t){
59243         var row;
59244         if((row = this.findRowIndex(t)) !== false){
59245             this.getRowComposite(row).addClass("x-grid-row-over");
59246         }
59247     },
59248
59249     onRowOut : function(e, t){
59250         var row;
59251         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
59252             this.getRowComposite(row).removeClass("x-grid-row-over");
59253         }
59254     },
59255
59256     renderHeaders : function(){
59257         var cm = this.cm;
59258         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
59259         var cb = [], lb = [], sb = [], lsb = [], p = {};
59260         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59261             p.cellId = "x-grid-hd-0-" + i;
59262             p.splitId = "x-grid-csplit-0-" + i;
59263             p.id = cm.getColumnId(i);
59264             p.value = cm.getColumnHeader(i) || "";
59265             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
59266             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
59267             if(!cm.isLocked(i)){
59268                 cb[cb.length] = ct.apply(p);
59269                 sb[sb.length] = st.apply(p);
59270             }else{
59271                 lb[lb.length] = ct.apply(p);
59272                 lsb[lsb.length] = st.apply(p);
59273             }
59274         }
59275         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
59276                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
59277     },
59278
59279     updateHeaders : function(){
59280         var html = this.renderHeaders();
59281         this.lockedHd.update(html[0]);
59282         this.mainHd.update(html[1]);
59283     },
59284
59285     /**
59286      * Focuses the specified row.
59287      * @param {Number} row The row index
59288      */
59289     focusRow : function(row)
59290     {
59291         //Roo.log('GridView.focusRow');
59292         var x = this.scroller.dom.scrollLeft;
59293         this.focusCell(row, 0, false);
59294         this.scroller.dom.scrollLeft = x;
59295     },
59296
59297     /**
59298      * Focuses the specified cell.
59299      * @param {Number} row The row index
59300      * @param {Number} col The column index
59301      * @param {Boolean} hscroll false to disable horizontal scrolling
59302      */
59303     focusCell : function(row, col, hscroll)
59304     {
59305         //Roo.log('GridView.focusCell');
59306         var el = this.ensureVisible(row, col, hscroll);
59307         this.focusEl.alignTo(el, "tl-tl");
59308         if(Roo.isGecko){
59309             this.focusEl.focus();
59310         }else{
59311             this.focusEl.focus.defer(1, this.focusEl);
59312         }
59313     },
59314
59315     /**
59316      * Scrolls the specified cell into view
59317      * @param {Number} row The row index
59318      * @param {Number} col The column index
59319      * @param {Boolean} hscroll false to disable horizontal scrolling
59320      */
59321     ensureVisible : function(row, col, hscroll)
59322     {
59323         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
59324         //return null; //disable for testing.
59325         if(typeof row != "number"){
59326             row = row.rowIndex;
59327         }
59328         if(row < 0 && row >= this.ds.getCount()){
59329             return  null;
59330         }
59331         col = (col !== undefined ? col : 0);
59332         var cm = this.grid.colModel;
59333         while(cm.isHidden(col)){
59334             col++;
59335         }
59336
59337         var el = this.getCell(row, col);
59338         if(!el){
59339             return null;
59340         }
59341         var c = this.scroller.dom;
59342
59343         var ctop = parseInt(el.offsetTop, 10);
59344         var cleft = parseInt(el.offsetLeft, 10);
59345         var cbot = ctop + el.offsetHeight;
59346         var cright = cleft + el.offsetWidth;
59347         
59348         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
59349         var stop = parseInt(c.scrollTop, 10);
59350         var sleft = parseInt(c.scrollLeft, 10);
59351         var sbot = stop + ch;
59352         var sright = sleft + c.clientWidth;
59353         /*
59354         Roo.log('GridView.ensureVisible:' +
59355                 ' ctop:' + ctop +
59356                 ' c.clientHeight:' + c.clientHeight +
59357                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
59358                 ' stop:' + stop +
59359                 ' cbot:' + cbot +
59360                 ' sbot:' + sbot +
59361                 ' ch:' + ch  
59362                 );
59363         */
59364         if(ctop < stop){
59365             c.scrollTop = ctop;
59366             //Roo.log("set scrolltop to ctop DISABLE?");
59367         }else if(cbot > sbot){
59368             //Roo.log("set scrolltop to cbot-ch");
59369             c.scrollTop = cbot-ch;
59370         }
59371         
59372         if(hscroll !== false){
59373             if(cleft < sleft){
59374                 c.scrollLeft = cleft;
59375             }else if(cright > sright){
59376                 c.scrollLeft = cright-c.clientWidth;
59377             }
59378         }
59379          
59380         return el;
59381     },
59382
59383     updateColumns : function(){
59384         this.grid.stopEditing();
59385         var cm = this.grid.colModel, colIds = this.getColumnIds();
59386         //var totalWidth = cm.getTotalWidth();
59387         var pos = 0;
59388         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59389             //if(cm.isHidden(i)) continue;
59390             var w = cm.getColumnWidth(i);
59391             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59392             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59393         }
59394         this.updateSplitters();
59395     },
59396
59397     generateRules : function(cm){
59398         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
59399         Roo.util.CSS.removeStyleSheet(rulesId);
59400         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59401             var cid = cm.getColumnId(i);
59402             var align = '';
59403             if(cm.config[i].align){
59404                 align = 'text-align:'+cm.config[i].align+';';
59405             }
59406             var hidden = '';
59407             if(cm.isHidden(i)){
59408                 hidden = 'display:none;';
59409             }
59410             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
59411             ruleBuf.push(
59412                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
59413                     this.hdSelector, cid, " {\n", align, width, "}\n",
59414                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
59415                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
59416         }
59417         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
59418     },
59419
59420     updateSplitters : function(){
59421         var cm = this.cm, s = this.getSplitters();
59422         if(s){ // splitters not created yet
59423             var pos = 0, locked = true;
59424             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59425                 if(cm.isHidden(i)) {
59426                     continue;
59427                 }
59428                 var w = cm.getColumnWidth(i); // make sure it's a number
59429                 if(!cm.isLocked(i) && locked){
59430                     pos = 0;
59431                     locked = false;
59432                 }
59433                 pos += w;
59434                 s[i].style.left = (pos-this.splitOffset) + "px";
59435             }
59436         }
59437     },
59438
59439     handleHiddenChange : function(colModel, colIndex, hidden){
59440         if(hidden){
59441             this.hideColumn(colIndex);
59442         }else{
59443             this.unhideColumn(colIndex);
59444         }
59445     },
59446
59447     hideColumn : function(colIndex){
59448         var cid = this.getColumnId(colIndex);
59449         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
59450         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
59451         if(Roo.isSafari){
59452             this.updateHeaders();
59453         }
59454         this.updateSplitters();
59455         this.layout();
59456     },
59457
59458     unhideColumn : function(colIndex){
59459         var cid = this.getColumnId(colIndex);
59460         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
59461         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
59462
59463         if(Roo.isSafari){
59464             this.updateHeaders();
59465         }
59466         this.updateSplitters();
59467         this.layout();
59468     },
59469
59470     insertRows : function(dm, firstRow, lastRow, isUpdate){
59471         if(firstRow == 0 && lastRow == dm.getCount()-1){
59472             this.refresh();
59473         }else{
59474             if(!isUpdate){
59475                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
59476             }
59477             var s = this.getScrollState();
59478             var markup = this.renderRows(firstRow, lastRow);
59479             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
59480             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
59481             this.restoreScroll(s);
59482             if(!isUpdate){
59483                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
59484                 this.syncRowHeights(firstRow, lastRow);
59485                 this.stripeRows(firstRow);
59486                 this.layout();
59487             }
59488         }
59489     },
59490
59491     bufferRows : function(markup, target, index){
59492         var before = null, trows = target.rows, tbody = target.tBodies[0];
59493         if(index < trows.length){
59494             before = trows[index];
59495         }
59496         var b = document.createElement("div");
59497         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
59498         var rows = b.firstChild.rows;
59499         for(var i = 0, len = rows.length; i < len; i++){
59500             if(before){
59501                 tbody.insertBefore(rows[0], before);
59502             }else{
59503                 tbody.appendChild(rows[0]);
59504             }
59505         }
59506         b.innerHTML = "";
59507         b = null;
59508     },
59509
59510     deleteRows : function(dm, firstRow, lastRow){
59511         if(dm.getRowCount()<1){
59512             this.fireEvent("beforerefresh", this);
59513             this.mainBody.update("");
59514             this.lockedBody.update("");
59515             this.fireEvent("refresh", this);
59516         }else{
59517             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
59518             var bt = this.getBodyTable();
59519             var tbody = bt.firstChild;
59520             var rows = bt.rows;
59521             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
59522                 tbody.removeChild(rows[firstRow]);
59523             }
59524             this.stripeRows(firstRow);
59525             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
59526         }
59527     },
59528
59529     updateRows : function(dataSource, firstRow, lastRow){
59530         var s = this.getScrollState();
59531         this.refresh();
59532         this.restoreScroll(s);
59533     },
59534
59535     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
59536         if(!noRefresh){
59537            this.refresh();
59538         }
59539         this.updateHeaderSortState();
59540     },
59541
59542     getScrollState : function(){
59543         
59544         var sb = this.scroller.dom;
59545         return {left: sb.scrollLeft, top: sb.scrollTop};
59546     },
59547
59548     stripeRows : function(startRow){
59549         if(!this.grid.stripeRows || this.ds.getCount() < 1){
59550             return;
59551         }
59552         startRow = startRow || 0;
59553         var rows = this.getBodyTable().rows;
59554         var lrows = this.getLockedTable().rows;
59555         var cls = ' x-grid-row-alt ';
59556         for(var i = startRow, len = rows.length; i < len; i++){
59557             var row = rows[i], lrow = lrows[i];
59558             var isAlt = ((i+1) % 2 == 0);
59559             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
59560             if(isAlt == hasAlt){
59561                 continue;
59562             }
59563             if(isAlt){
59564                 row.className += " x-grid-row-alt";
59565             }else{
59566                 row.className = row.className.replace("x-grid-row-alt", "");
59567             }
59568             if(lrow){
59569                 lrow.className = row.className;
59570             }
59571         }
59572     },
59573
59574     restoreScroll : function(state){
59575         //Roo.log('GridView.restoreScroll');
59576         var sb = this.scroller.dom;
59577         sb.scrollLeft = state.left;
59578         sb.scrollTop = state.top;
59579         this.syncScroll();
59580     },
59581
59582     syncScroll : function(){
59583         //Roo.log('GridView.syncScroll');
59584         var sb = this.scroller.dom;
59585         var sh = this.mainHd.dom;
59586         var bs = this.mainBody.dom;
59587         var lv = this.lockedBody.dom;
59588         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
59589         lv.scrollTop = bs.scrollTop = sb.scrollTop;
59590     },
59591
59592     handleScroll : function(e){
59593         this.syncScroll();
59594         var sb = this.scroller.dom;
59595         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
59596         e.stopEvent();
59597     },
59598
59599     handleWheel : function(e){
59600         var d = e.getWheelDelta();
59601         this.scroller.dom.scrollTop -= d*22;
59602         // set this here to prevent jumpy scrolling on large tables
59603         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
59604         e.stopEvent();
59605     },
59606
59607     renderRows : function(startRow, endRow){
59608         // pull in all the crap needed to render rows
59609         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
59610         var colCount = cm.getColumnCount();
59611
59612         if(ds.getCount() < 1){
59613             return ["", ""];
59614         }
59615
59616         // build a map for all the columns
59617         var cs = [];
59618         for(var i = 0; i < colCount; i++){
59619             var name = cm.getDataIndex(i);
59620             cs[i] = {
59621                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
59622                 renderer : cm.getRenderer(i),
59623                 id : cm.getColumnId(i),
59624                 locked : cm.isLocked(i),
59625                 has_editor : cm.isCellEditable(i)
59626             };
59627         }
59628
59629         startRow = startRow || 0;
59630         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
59631
59632         // records to render
59633         var rs = ds.getRange(startRow, endRow);
59634
59635         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
59636     },
59637
59638     // As much as I hate to duplicate code, this was branched because FireFox really hates
59639     // [].join("") on strings. The performance difference was substantial enough to
59640     // branch this function
59641     doRender : Roo.isGecko ?
59642             function(cs, rs, ds, startRow, colCount, stripe){
59643                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59644                 // buffers
59645                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59646                 
59647                 var hasListener = this.grid.hasListener('rowclass');
59648                 var rowcfg = {};
59649                 for(var j = 0, len = rs.length; j < len; j++){
59650                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
59651                     for(var i = 0; i < colCount; i++){
59652                         c = cs[i];
59653                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59654                         p.id = c.id;
59655                         p.css = p.attr = "";
59656                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59657                         if(p.value == undefined || p.value === "") {
59658                             p.value = "&#160;";
59659                         }
59660                         if(c.has_editor){
59661                             p.css += ' x-grid-editable-cell';
59662                         }
59663                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
59664                             p.css +=  ' x-grid-dirty-cell';
59665                         }
59666                         var markup = ct.apply(p);
59667                         if(!c.locked){
59668                             cb+= markup;
59669                         }else{
59670                             lcb+= markup;
59671                         }
59672                     }
59673                     var alt = [];
59674                     if(stripe && ((rowIndex+1) % 2 == 0)){
59675                         alt.push("x-grid-row-alt")
59676                     }
59677                     if(r.dirty){
59678                         alt.push(  " x-grid-dirty-row");
59679                     }
59680                     rp.cells = lcb;
59681                     if(this.getRowClass){
59682                         alt.push(this.getRowClass(r, rowIndex));
59683                     }
59684                     if (hasListener) {
59685                         rowcfg = {
59686                              
59687                             record: r,
59688                             rowIndex : rowIndex,
59689                             rowClass : ''
59690                         };
59691                         this.grid.fireEvent('rowclass', this, rowcfg);
59692                         alt.push(rowcfg.rowClass);
59693                     }
59694                     rp.alt = alt.join(" ");
59695                     lbuf+= rt.apply(rp);
59696                     rp.cells = cb;
59697                     buf+=  rt.apply(rp);
59698                 }
59699                 return [lbuf, buf];
59700             } :
59701             function(cs, rs, ds, startRow, colCount, stripe){
59702                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59703                 // buffers
59704                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59705                 var hasListener = this.grid.hasListener('rowclass');
59706  
59707                 var rowcfg = {};
59708                 for(var j = 0, len = rs.length; j < len; j++){
59709                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
59710                     for(var i = 0; i < colCount; i++){
59711                         c = cs[i];
59712                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59713                         p.id = c.id;
59714                         p.css = p.attr = "";
59715                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59716                         if(p.value == undefined || p.value === "") {
59717                             p.value = "&#160;";
59718                         }
59719                         //Roo.log(c);
59720                          if(c.has_editor){
59721                             p.css += ' x-grid-editable-cell';
59722                         }
59723                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
59724                             p.css += ' x-grid-dirty-cell' 
59725                         }
59726                         
59727                         var markup = ct.apply(p);
59728                         if(!c.locked){
59729                             cb[cb.length] = markup;
59730                         }else{
59731                             lcb[lcb.length] = markup;
59732                         }
59733                     }
59734                     var alt = [];
59735                     if(stripe && ((rowIndex+1) % 2 == 0)){
59736                         alt.push( "x-grid-row-alt");
59737                     }
59738                     if(r.dirty){
59739                         alt.push(" x-grid-dirty-row");
59740                     }
59741                     rp.cells = lcb;
59742                     if(this.getRowClass){
59743                         alt.push( this.getRowClass(r, rowIndex));
59744                     }
59745                     if (hasListener) {
59746                         rowcfg = {
59747                              
59748                             record: r,
59749                             rowIndex : rowIndex,
59750                             rowClass : ''
59751                         };
59752                         this.grid.fireEvent('rowclass', this, rowcfg);
59753                         alt.push(rowcfg.rowClass);
59754                     }
59755                     
59756                     rp.alt = alt.join(" ");
59757                     rp.cells = lcb.join("");
59758                     lbuf[lbuf.length] = rt.apply(rp);
59759                     rp.cells = cb.join("");
59760                     buf[buf.length] =  rt.apply(rp);
59761                 }
59762                 return [lbuf.join(""), buf.join("")];
59763             },
59764
59765     renderBody : function(){
59766         var markup = this.renderRows();
59767         var bt = this.templates.body;
59768         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
59769     },
59770
59771     /**
59772      * Refreshes the grid
59773      * @param {Boolean} headersToo
59774      */
59775     refresh : function(headersToo){
59776         this.fireEvent("beforerefresh", this);
59777         this.grid.stopEditing();
59778         var result = this.renderBody();
59779         this.lockedBody.update(result[0]);
59780         this.mainBody.update(result[1]);
59781         if(headersToo === true){
59782             this.updateHeaders();
59783             this.updateColumns();
59784             this.updateSplitters();
59785             this.updateHeaderSortState();
59786         }
59787         this.syncRowHeights();
59788         this.layout();
59789         this.fireEvent("refresh", this);
59790     },
59791
59792     handleColumnMove : function(cm, oldIndex, newIndex){
59793         this.indexMap = null;
59794         var s = this.getScrollState();
59795         this.refresh(true);
59796         this.restoreScroll(s);
59797         this.afterMove(newIndex);
59798     },
59799
59800     afterMove : function(colIndex){
59801         if(this.enableMoveAnim && Roo.enableFx){
59802             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
59803         }
59804         // if multisort - fix sortOrder, and reload..
59805         if (this.grid.dataSource.multiSort) {
59806             // the we can call sort again..
59807             var dm = this.grid.dataSource;
59808             var cm = this.grid.colModel;
59809             var so = [];
59810             for(var i = 0; i < cm.config.length; i++ ) {
59811                 
59812                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
59813                     continue; // dont' bother, it's not in sort list or being set.
59814                 }
59815                 
59816                 so.push(cm.config[i].dataIndex);
59817             };
59818             dm.sortOrder = so;
59819             dm.load(dm.lastOptions);
59820             
59821             
59822         }
59823         
59824     },
59825
59826     updateCell : function(dm, rowIndex, dataIndex){
59827         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
59828         if(typeof colIndex == "undefined"){ // not present in grid
59829             return;
59830         }
59831         var cm = this.grid.colModel;
59832         var cell = this.getCell(rowIndex, colIndex);
59833         var cellText = this.getCellText(rowIndex, colIndex);
59834
59835         var p = {
59836             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
59837             id : cm.getColumnId(colIndex),
59838             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
59839         };
59840         var renderer = cm.getRenderer(colIndex);
59841         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
59842         if(typeof val == "undefined" || val === "") {
59843             val = "&#160;";
59844         }
59845         cellText.innerHTML = val;
59846         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
59847         this.syncRowHeights(rowIndex, rowIndex);
59848     },
59849
59850     calcColumnWidth : function(colIndex, maxRowsToMeasure){
59851         var maxWidth = 0;
59852         if(this.grid.autoSizeHeaders){
59853             var h = this.getHeaderCellMeasure(colIndex);
59854             maxWidth = Math.max(maxWidth, h.scrollWidth);
59855         }
59856         var tb, index;
59857         if(this.cm.isLocked(colIndex)){
59858             tb = this.getLockedTable();
59859             index = colIndex;
59860         }else{
59861             tb = this.getBodyTable();
59862             index = colIndex - this.cm.getLockedCount();
59863         }
59864         if(tb && tb.rows){
59865             var rows = tb.rows;
59866             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
59867             for(var i = 0; i < stopIndex; i++){
59868                 var cell = rows[i].childNodes[index].firstChild;
59869                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
59870             }
59871         }
59872         return maxWidth + /*margin for error in IE*/ 5;
59873     },
59874     /**
59875      * Autofit a column to its content.
59876      * @param {Number} colIndex
59877      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
59878      */
59879      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
59880          if(this.cm.isHidden(colIndex)){
59881              return; // can't calc a hidden column
59882          }
59883         if(forceMinSize){
59884             var cid = this.cm.getColumnId(colIndex);
59885             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
59886            if(this.grid.autoSizeHeaders){
59887                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
59888            }
59889         }
59890         var newWidth = this.calcColumnWidth(colIndex);
59891         this.cm.setColumnWidth(colIndex,
59892             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
59893         if(!suppressEvent){
59894             this.grid.fireEvent("columnresize", colIndex, newWidth);
59895         }
59896     },
59897
59898     /**
59899      * Autofits all columns to their content and then expands to fit any extra space in the grid
59900      */
59901      autoSizeColumns : function(){
59902         var cm = this.grid.colModel;
59903         var colCount = cm.getColumnCount();
59904         for(var i = 0; i < colCount; i++){
59905             this.autoSizeColumn(i, true, true);
59906         }
59907         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
59908             this.fitColumns();
59909         }else{
59910             this.updateColumns();
59911             this.layout();
59912         }
59913     },
59914
59915     /**
59916      * Autofits all columns to the grid's width proportionate with their current size
59917      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
59918      */
59919     fitColumns : function(reserveScrollSpace){
59920         var cm = this.grid.colModel;
59921         var colCount = cm.getColumnCount();
59922         var cols = [];
59923         var width = 0;
59924         var i, w;
59925         for (i = 0; i < colCount; i++){
59926             if(!cm.isHidden(i) && !cm.isFixed(i)){
59927                 w = cm.getColumnWidth(i);
59928                 cols.push(i);
59929                 cols.push(w);
59930                 width += w;
59931             }
59932         }
59933         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
59934         if(reserveScrollSpace){
59935             avail -= 17;
59936         }
59937         var frac = (avail - cm.getTotalWidth())/width;
59938         while (cols.length){
59939             w = cols.pop();
59940             i = cols.pop();
59941             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
59942         }
59943         this.updateColumns();
59944         this.layout();
59945     },
59946
59947     onRowSelect : function(rowIndex){
59948         var row = this.getRowComposite(rowIndex);
59949         row.addClass("x-grid-row-selected");
59950     },
59951
59952     onRowDeselect : function(rowIndex){
59953         var row = this.getRowComposite(rowIndex);
59954         row.removeClass("x-grid-row-selected");
59955     },
59956
59957     onCellSelect : function(row, col){
59958         var cell = this.getCell(row, col);
59959         if(cell){
59960             Roo.fly(cell).addClass("x-grid-cell-selected");
59961         }
59962     },
59963
59964     onCellDeselect : function(row, col){
59965         var cell = this.getCell(row, col);
59966         if(cell){
59967             Roo.fly(cell).removeClass("x-grid-cell-selected");
59968         }
59969     },
59970
59971     updateHeaderSortState : function(){
59972         
59973         // sort state can be single { field: xxx, direction : yyy}
59974         // or   { xxx=>ASC , yyy : DESC ..... }
59975         
59976         var mstate = {};
59977         if (!this.ds.multiSort) { 
59978             var state = this.ds.getSortState();
59979             if(!state){
59980                 return;
59981             }
59982             mstate[state.field] = state.direction;
59983             // FIXME... - this is not used here.. but might be elsewhere..
59984             this.sortState = state;
59985             
59986         } else {
59987             mstate = this.ds.sortToggle;
59988         }
59989         //remove existing sort classes..
59990         
59991         var sc = this.sortClasses;
59992         var hds = this.el.select(this.headerSelector).removeClass(sc);
59993         
59994         for(var f in mstate) {
59995         
59996             var sortColumn = this.cm.findColumnIndex(f);
59997             
59998             if(sortColumn != -1){
59999                 var sortDir = mstate[f];        
60000                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
60001             }
60002         }
60003         
60004          
60005         
60006     },
60007
60008
60009     handleHeaderClick : function(g, index,e){
60010         
60011         Roo.log("header click");
60012         
60013         if (Roo.isTouch) {
60014             // touch events on header are handled by context
60015             this.handleHdCtx(g,index,e);
60016             return;
60017         }
60018         
60019         
60020         if(this.headersDisabled){
60021             return;
60022         }
60023         var dm = g.dataSource, cm = g.colModel;
60024         if(!cm.isSortable(index)){
60025             return;
60026         }
60027         g.stopEditing();
60028         
60029         if (dm.multiSort) {
60030             // update the sortOrder
60031             var so = [];
60032             for(var i = 0; i < cm.config.length; i++ ) {
60033                 
60034                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
60035                     continue; // dont' bother, it's not in sort list or being set.
60036                 }
60037                 
60038                 so.push(cm.config[i].dataIndex);
60039             };
60040             dm.sortOrder = so;
60041         }
60042         
60043         
60044         dm.sort(cm.getDataIndex(index));
60045     },
60046
60047
60048     destroy : function(){
60049         if(this.colMenu){
60050             this.colMenu.removeAll();
60051             Roo.menu.MenuMgr.unregister(this.colMenu);
60052             this.colMenu.getEl().remove();
60053             delete this.colMenu;
60054         }
60055         if(this.hmenu){
60056             this.hmenu.removeAll();
60057             Roo.menu.MenuMgr.unregister(this.hmenu);
60058             this.hmenu.getEl().remove();
60059             delete this.hmenu;
60060         }
60061         if(this.grid.enableColumnMove){
60062             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60063             if(dds){
60064                 for(var dd in dds){
60065                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
60066                         var elid = dds[dd].dragElId;
60067                         dds[dd].unreg();
60068                         Roo.get(elid).remove();
60069                     } else if(dds[dd].config.isTarget){
60070                         dds[dd].proxyTop.remove();
60071                         dds[dd].proxyBottom.remove();
60072                         dds[dd].unreg();
60073                     }
60074                     if(Roo.dd.DDM.locationCache[dd]){
60075                         delete Roo.dd.DDM.locationCache[dd];
60076                     }
60077                 }
60078                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60079             }
60080         }
60081         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
60082         this.bind(null, null);
60083         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
60084     },
60085
60086     handleLockChange : function(){
60087         this.refresh(true);
60088     },
60089
60090     onDenyColumnLock : function(){
60091
60092     },
60093
60094     onDenyColumnHide : function(){
60095
60096     },
60097
60098     handleHdMenuClick : function(item){
60099         var index = this.hdCtxIndex;
60100         var cm = this.cm, ds = this.ds;
60101         switch(item.id){
60102             case "asc":
60103                 ds.sort(cm.getDataIndex(index), "ASC");
60104                 break;
60105             case "desc":
60106                 ds.sort(cm.getDataIndex(index), "DESC");
60107                 break;
60108             case "lock":
60109                 var lc = cm.getLockedCount();
60110                 if(cm.getColumnCount(true) <= lc+1){
60111                     this.onDenyColumnLock();
60112                     return;
60113                 }
60114                 if(lc != index){
60115                     cm.setLocked(index, true, true);
60116                     cm.moveColumn(index, lc);
60117                     this.grid.fireEvent("columnmove", index, lc);
60118                 }else{
60119                     cm.setLocked(index, true);
60120                 }
60121             break;
60122             case "unlock":
60123                 var lc = cm.getLockedCount();
60124                 if((lc-1) != index){
60125                     cm.setLocked(index, false, true);
60126                     cm.moveColumn(index, lc-1);
60127                     this.grid.fireEvent("columnmove", index, lc-1);
60128                 }else{
60129                     cm.setLocked(index, false);
60130                 }
60131             break;
60132             case 'wider': // used to expand cols on touch..
60133             case 'narrow':
60134                 var cw = cm.getColumnWidth(index);
60135                 cw += (item.id == 'wider' ? 1 : -1) * 50;
60136                 cw = Math.max(0, cw);
60137                 cw = Math.min(cw,4000);
60138                 cm.setColumnWidth(index, cw);
60139                 break;
60140                 
60141             default:
60142                 index = cm.getIndexById(item.id.substr(4));
60143                 if(index != -1){
60144                     if(item.checked && cm.getColumnCount(true) <= 1){
60145                         this.onDenyColumnHide();
60146                         return false;
60147                     }
60148                     cm.setHidden(index, item.checked);
60149                 }
60150         }
60151         return true;
60152     },
60153
60154     beforeColMenuShow : function(){
60155         var cm = this.cm,  colCount = cm.getColumnCount();
60156         this.colMenu.removeAll();
60157         
60158         var items = [];
60159         for(var i = 0; i < colCount; i++){
60160             items.push({
60161                 id: "col-"+cm.getColumnId(i),
60162                 text: cm.getColumnHeader(i),
60163                 checked: !cm.isHidden(i),
60164                 hideOnClick:false
60165             });
60166         }
60167         
60168         if (this.grid.sortColMenu) {
60169             items.sort(function(a,b) {
60170                 if (a.text == b.text) {
60171                     return 0;
60172                 }
60173                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
60174             });
60175         }
60176         
60177         for(var i = 0; i < colCount; i++){
60178             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
60179         }
60180     },
60181
60182     handleHdCtx : function(g, index, e){
60183         e.stopEvent();
60184         var hd = this.getHeaderCell(index);
60185         this.hdCtxIndex = index;
60186         var ms = this.hmenu.items, cm = this.cm;
60187         ms.get("asc").setDisabled(!cm.isSortable(index));
60188         ms.get("desc").setDisabled(!cm.isSortable(index));
60189         if(this.grid.enableColLock !== false){
60190             ms.get("lock").setDisabled(cm.isLocked(index));
60191             ms.get("unlock").setDisabled(!cm.isLocked(index));
60192         }
60193         this.hmenu.show(hd, "tl-bl");
60194     },
60195
60196     handleHdOver : function(e){
60197         var hd = this.findHeaderCell(e.getTarget());
60198         if(hd && !this.headersDisabled){
60199             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
60200                this.fly(hd).addClass("x-grid-hd-over");
60201             }
60202         }
60203     },
60204
60205     handleHdOut : function(e){
60206         var hd = this.findHeaderCell(e.getTarget());
60207         if(hd){
60208             this.fly(hd).removeClass("x-grid-hd-over");
60209         }
60210     },
60211
60212     handleSplitDblClick : function(e, t){
60213         var i = this.getCellIndex(t);
60214         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
60215             this.autoSizeColumn(i, true);
60216             this.layout();
60217         }
60218     },
60219
60220     render : function(){
60221
60222         var cm = this.cm;
60223         var colCount = cm.getColumnCount();
60224
60225         if(this.grid.monitorWindowResize === true){
60226             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60227         }
60228         var header = this.renderHeaders();
60229         var body = this.templates.body.apply({rows:""});
60230         var html = this.templates.master.apply({
60231             lockedBody: body,
60232             body: body,
60233             lockedHeader: header[0],
60234             header: header[1]
60235         });
60236
60237         //this.updateColumns();
60238
60239         this.grid.getGridEl().dom.innerHTML = html;
60240
60241         this.initElements();
60242         
60243         // a kludge to fix the random scolling effect in webkit
60244         this.el.on("scroll", function() {
60245             this.el.dom.scrollTop=0; // hopefully not recursive..
60246         },this);
60247
60248         this.scroller.on("scroll", this.handleScroll, this);
60249         this.lockedBody.on("mousewheel", this.handleWheel, this);
60250         this.mainBody.on("mousewheel", this.handleWheel, this);
60251
60252         this.mainHd.on("mouseover", this.handleHdOver, this);
60253         this.mainHd.on("mouseout", this.handleHdOut, this);
60254         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
60255                 {delegate: "."+this.splitClass});
60256
60257         this.lockedHd.on("mouseover", this.handleHdOver, this);
60258         this.lockedHd.on("mouseout", this.handleHdOut, this);
60259         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
60260                 {delegate: "."+this.splitClass});
60261
60262         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
60263             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60264         }
60265
60266         this.updateSplitters();
60267
60268         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
60269             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60270             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60271         }
60272
60273         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
60274             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
60275             this.hmenu.add(
60276                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
60277                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
60278             );
60279             if(this.grid.enableColLock !== false){
60280                 this.hmenu.add('-',
60281                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
60282                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
60283                 );
60284             }
60285             if (Roo.isTouch) {
60286                  this.hmenu.add('-',
60287                     {id:"wider", text: this.columnsWiderText},
60288                     {id:"narrow", text: this.columnsNarrowText }
60289                 );
60290                 
60291                  
60292             }
60293             
60294             if(this.grid.enableColumnHide !== false){
60295
60296                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
60297                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
60298                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
60299
60300                 this.hmenu.add('-',
60301                     {id:"columns", text: this.columnsText, menu: this.colMenu}
60302                 );
60303             }
60304             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
60305
60306             this.grid.on("headercontextmenu", this.handleHdCtx, this);
60307         }
60308
60309         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
60310             this.dd = new Roo.grid.GridDragZone(this.grid, {
60311                 ddGroup : this.grid.ddGroup || 'GridDD'
60312             });
60313             
60314         }
60315
60316         /*
60317         for(var i = 0; i < colCount; i++){
60318             if(cm.isHidden(i)){
60319                 this.hideColumn(i);
60320             }
60321             if(cm.config[i].align){
60322                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
60323                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
60324             }
60325         }*/
60326         
60327         this.updateHeaderSortState();
60328
60329         this.beforeInitialResize();
60330         this.layout(true);
60331
60332         // two part rendering gives faster view to the user
60333         this.renderPhase2.defer(1, this);
60334     },
60335
60336     renderPhase2 : function(){
60337         // render the rows now
60338         this.refresh();
60339         if(this.grid.autoSizeColumns){
60340             this.autoSizeColumns();
60341         }
60342     },
60343
60344     beforeInitialResize : function(){
60345
60346     },
60347
60348     onColumnSplitterMoved : function(i, w){
60349         this.userResized = true;
60350         var cm = this.grid.colModel;
60351         cm.setColumnWidth(i, w, true);
60352         var cid = cm.getColumnId(i);
60353         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60354         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60355         this.updateSplitters();
60356         this.layout();
60357         this.grid.fireEvent("columnresize", i, w);
60358     },
60359
60360     syncRowHeights : function(startIndex, endIndex){
60361         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
60362             startIndex = startIndex || 0;
60363             var mrows = this.getBodyTable().rows;
60364             var lrows = this.getLockedTable().rows;
60365             var len = mrows.length-1;
60366             endIndex = Math.min(endIndex || len, len);
60367             for(var i = startIndex; i <= endIndex; i++){
60368                 var m = mrows[i], l = lrows[i];
60369                 var h = Math.max(m.offsetHeight, l.offsetHeight);
60370                 m.style.height = l.style.height = h + "px";
60371             }
60372         }
60373     },
60374
60375     layout : function(initialRender, is2ndPass)
60376     {
60377         var g = this.grid;
60378         var auto = g.autoHeight;
60379         var scrollOffset = 16;
60380         var c = g.getGridEl(), cm = this.cm,
60381                 expandCol = g.autoExpandColumn,
60382                 gv = this;
60383         //c.beginMeasure();
60384
60385         if(!c.dom.offsetWidth){ // display:none?
60386             if(initialRender){
60387                 this.lockedWrap.show();
60388                 this.mainWrap.show();
60389             }
60390             return;
60391         }
60392
60393         var hasLock = this.cm.isLocked(0);
60394
60395         var tbh = this.headerPanel.getHeight();
60396         var bbh = this.footerPanel.getHeight();
60397
60398         if(auto){
60399             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
60400             var newHeight = ch + c.getBorderWidth("tb");
60401             if(g.maxHeight){
60402                 newHeight = Math.min(g.maxHeight, newHeight);
60403             }
60404             c.setHeight(newHeight);
60405         }
60406
60407         if(g.autoWidth){
60408             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
60409         }
60410
60411         var s = this.scroller;
60412
60413         var csize = c.getSize(true);
60414
60415         this.el.setSize(csize.width, csize.height);
60416
60417         this.headerPanel.setWidth(csize.width);
60418         this.footerPanel.setWidth(csize.width);
60419
60420         var hdHeight = this.mainHd.getHeight();
60421         var vw = csize.width;
60422         var vh = csize.height - (tbh + bbh);
60423
60424         s.setSize(vw, vh);
60425
60426         var bt = this.getBodyTable();
60427         
60428         if(cm.getLockedCount() == cm.config.length){
60429             bt = this.getLockedTable();
60430         }
60431         
60432         var ltWidth = hasLock ?
60433                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
60434
60435         var scrollHeight = bt.offsetHeight;
60436         var scrollWidth = ltWidth + bt.offsetWidth;
60437         var vscroll = false, hscroll = false;
60438
60439         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
60440
60441         var lw = this.lockedWrap, mw = this.mainWrap;
60442         var lb = this.lockedBody, mb = this.mainBody;
60443
60444         setTimeout(function(){
60445             var t = s.dom.offsetTop;
60446             var w = s.dom.clientWidth,
60447                 h = s.dom.clientHeight;
60448
60449             lw.setTop(t);
60450             lw.setSize(ltWidth, h);
60451
60452             mw.setLeftTop(ltWidth, t);
60453             mw.setSize(w-ltWidth, h);
60454
60455             lb.setHeight(h-hdHeight);
60456             mb.setHeight(h-hdHeight);
60457
60458             if(is2ndPass !== true && !gv.userResized && expandCol){
60459                 // high speed resize without full column calculation
60460                 
60461                 var ci = cm.getIndexById(expandCol);
60462                 if (ci < 0) {
60463                     ci = cm.findColumnIndex(expandCol);
60464                 }
60465                 ci = Math.max(0, ci); // make sure it's got at least the first col.
60466                 var expandId = cm.getColumnId(ci);
60467                 var  tw = cm.getTotalWidth(false);
60468                 var currentWidth = cm.getColumnWidth(ci);
60469                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
60470                 if(currentWidth != cw){
60471                     cm.setColumnWidth(ci, cw, true);
60472                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60473                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60474                     gv.updateSplitters();
60475                     gv.layout(false, true);
60476                 }
60477             }
60478
60479             if(initialRender){
60480                 lw.show();
60481                 mw.show();
60482             }
60483             //c.endMeasure();
60484         }, 10);
60485     },
60486
60487     onWindowResize : function(){
60488         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
60489             return;
60490         }
60491         this.layout();
60492     },
60493
60494     appendFooter : function(parentEl){
60495         return null;
60496     },
60497
60498     sortAscText : "Sort Ascending",
60499     sortDescText : "Sort Descending",
60500     lockText : "Lock Column",
60501     unlockText : "Unlock Column",
60502     columnsText : "Columns",
60503  
60504     columnsWiderText : "Wider",
60505     columnsNarrowText : "Thinner"
60506 });
60507
60508
60509 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
60510     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
60511     this.proxy.el.addClass('x-grid3-col-dd');
60512 };
60513
60514 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
60515     handleMouseDown : function(e){
60516
60517     },
60518
60519     callHandleMouseDown : function(e){
60520         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
60521     }
60522 });
60523 /*
60524  * Based on:
60525  * Ext JS Library 1.1.1
60526  * Copyright(c) 2006-2007, Ext JS, LLC.
60527  *
60528  * Originally Released Under LGPL - original licence link has changed is not relivant.
60529  *
60530  * Fork - LGPL
60531  * <script type="text/javascript">
60532  */
60533  /**
60534  * @extends Roo.dd.DDProxy
60535  * @class Roo.grid.SplitDragZone
60536  * Support for Column Header resizing
60537  * @constructor
60538  * @param {Object} config
60539  */
60540 // private
60541 // This is a support class used internally by the Grid components
60542 Roo.grid.SplitDragZone = function(grid, hd, hd2){
60543     this.grid = grid;
60544     this.view = grid.getView();
60545     this.proxy = this.view.resizeProxy;
60546     Roo.grid.SplitDragZone.superclass.constructor.call(
60547         this,
60548         hd, // ID
60549         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
60550         {  // CONFIG
60551             dragElId : Roo.id(this.proxy.dom),
60552             resizeFrame:false
60553         }
60554     );
60555     
60556     this.setHandleElId(Roo.id(hd));
60557     if (hd2 !== false) {
60558         this.setOuterHandleElId(Roo.id(hd2));
60559     }
60560     
60561     this.scroll = false;
60562 };
60563 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
60564     fly: Roo.Element.fly,
60565
60566     b4StartDrag : function(x, y){
60567         this.view.headersDisabled = true;
60568         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
60569                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
60570         );
60571         this.proxy.setHeight(h);
60572         
60573         // for old system colWidth really stored the actual width?
60574         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
60575         // which in reality did not work.. - it worked only for fixed sizes
60576         // for resizable we need to use actual sizes.
60577         var w = this.cm.getColumnWidth(this.cellIndex);
60578         if (!this.view.mainWrap) {
60579             // bootstrap.
60580             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
60581         }
60582         
60583         
60584         
60585         // this was w-this.grid.minColumnWidth;
60586         // doesnt really make sense? - w = thie curren width or the rendered one?
60587         var minw = Math.max(w-this.grid.minColumnWidth, 0);
60588         this.resetConstraints();
60589         this.setXConstraint(minw, 1000);
60590         this.setYConstraint(0, 0);
60591         this.minX = x - minw;
60592         this.maxX = x + 1000;
60593         this.startPos = x;
60594         if (!this.view.mainWrap) { // this is Bootstrap code..
60595             this.getDragEl().style.display='block';
60596         }
60597         
60598         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
60599     },
60600
60601
60602     handleMouseDown : function(e){
60603         ev = Roo.EventObject.setEvent(e);
60604         var t = this.fly(ev.getTarget());
60605         if(t.hasClass("x-grid-split")){
60606             this.cellIndex = this.view.getCellIndex(t.dom);
60607             this.split = t.dom;
60608             this.cm = this.grid.colModel;
60609             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
60610                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
60611             }
60612         }
60613     },
60614
60615     endDrag : function(e){
60616         this.view.headersDisabled = false;
60617         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
60618         var diff = endX - this.startPos;
60619         // 
60620         var w = this.cm.getColumnWidth(this.cellIndex);
60621         if (!this.view.mainWrap) {
60622             w = 0;
60623         }
60624         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
60625     },
60626
60627     autoOffset : function(){
60628         this.setDelta(0,0);
60629     }
60630 });/*
60631  * Based on:
60632  * Ext JS Library 1.1.1
60633  * Copyright(c) 2006-2007, Ext JS, LLC.
60634  *
60635  * Originally Released Under LGPL - original licence link has changed is not relivant.
60636  *
60637  * Fork - LGPL
60638  * <script type="text/javascript">
60639  */
60640  
60641 // private
60642 // This is a support class used internally by the Grid components
60643 Roo.grid.GridDragZone = function(grid, config){
60644     this.view = grid.getView();
60645     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
60646     if(this.view.lockedBody){
60647         this.setHandleElId(Roo.id(this.view.mainBody.dom));
60648         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
60649     }
60650     this.scroll = false;
60651     this.grid = grid;
60652     this.ddel = document.createElement('div');
60653     this.ddel.className = 'x-grid-dd-wrap';
60654 };
60655
60656 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
60657     ddGroup : "GridDD",
60658
60659     getDragData : function(e){
60660         var t = Roo.lib.Event.getTarget(e);
60661         var rowIndex = this.view.findRowIndex(t);
60662         var sm = this.grid.selModel;
60663             
60664         //Roo.log(rowIndex);
60665         
60666         if (sm.getSelectedCell) {
60667             // cell selection..
60668             if (!sm.getSelectedCell()) {
60669                 return false;
60670             }
60671             if (rowIndex != sm.getSelectedCell()[0]) {
60672                 return false;
60673             }
60674         
60675         }
60676         if (sm.getSelections && sm.getSelections().length < 1) {
60677             return false;
60678         }
60679         
60680         
60681         // before it used to all dragging of unseleted... - now we dont do that.
60682         if(rowIndex !== false){
60683             
60684             // if editorgrid.. 
60685             
60686             
60687             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
60688                
60689             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
60690               //  
60691             //}
60692             if (e.hasModifier()){
60693                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
60694             }
60695             
60696             Roo.log("getDragData");
60697             
60698             return {
60699                 grid: this.grid,
60700                 ddel: this.ddel,
60701                 rowIndex: rowIndex,
60702                 selections: sm.getSelections ? sm.getSelections() : (
60703                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
60704             };
60705         }
60706         return false;
60707     },
60708     
60709     
60710     onInitDrag : function(e){
60711         var data = this.dragData;
60712         this.ddel.innerHTML = this.grid.getDragDropText();
60713         this.proxy.update(this.ddel);
60714         // fire start drag?
60715     },
60716
60717     afterRepair : function(){
60718         this.dragging = false;
60719     },
60720
60721     getRepairXY : function(e, data){
60722         return false;
60723     },
60724
60725     onEndDrag : function(data, e){
60726         // fire end drag?
60727     },
60728
60729     onValidDrop : function(dd, e, id){
60730         // fire drag drop?
60731         this.hideProxy();
60732     },
60733
60734     beforeInvalidDrop : function(e, id){
60735
60736     }
60737 });/*
60738  * Based on:
60739  * Ext JS Library 1.1.1
60740  * Copyright(c) 2006-2007, Ext JS, LLC.
60741  *
60742  * Originally Released Under LGPL - original licence link has changed is not relivant.
60743  *
60744  * Fork - LGPL
60745  * <script type="text/javascript">
60746  */
60747  
60748
60749 /**
60750  * @class Roo.grid.ColumnModel
60751  * @extends Roo.util.Observable
60752  * This is the default implementation of a ColumnModel used by the Grid. It defines
60753  * the columns in the grid.
60754  * <br>Usage:<br>
60755  <pre><code>
60756  var colModel = new Roo.grid.ColumnModel([
60757         {header: "Ticker", width: 60, sortable: true, locked: true},
60758         {header: "Company Name", width: 150, sortable: true},
60759         {header: "Market Cap.", width: 100, sortable: true},
60760         {header: "$ Sales", width: 100, sortable: true, renderer: money},
60761         {header: "Employees", width: 100, sortable: true, resizable: false}
60762  ]);
60763  </code></pre>
60764  * <p>
60765  
60766  * The config options listed for this class are options which may appear in each
60767  * individual column definition.
60768  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
60769  * @constructor
60770  * @param {Object} config An Array of column config objects. See this class's
60771  * config objects for details.
60772 */
60773 Roo.grid.ColumnModel = function(config){
60774         /**
60775      * The config passed into the constructor
60776      */
60777     this.config = []; //config;
60778     this.lookup = {};
60779
60780     // if no id, create one
60781     // if the column does not have a dataIndex mapping,
60782     // map it to the order it is in the config
60783     for(var i = 0, len = config.length; i < len; i++){
60784         this.addColumn(config[i]);
60785         
60786     }
60787
60788     /**
60789      * The width of columns which have no width specified (defaults to 100)
60790      * @type Number
60791      */
60792     this.defaultWidth = 100;
60793
60794     /**
60795      * Default sortable of columns which have no sortable specified (defaults to false)
60796      * @type Boolean
60797      */
60798     this.defaultSortable = false;
60799
60800     this.addEvents({
60801         /**
60802              * @event widthchange
60803              * Fires when the width of a column changes.
60804              * @param {ColumnModel} this
60805              * @param {Number} columnIndex The column index
60806              * @param {Number} newWidth The new width
60807              */
60808             "widthchange": true,
60809         /**
60810              * @event headerchange
60811              * Fires when the text of a header changes.
60812              * @param {ColumnModel} this
60813              * @param {Number} columnIndex The column index
60814              * @param {Number} newText The new header text
60815              */
60816             "headerchange": true,
60817         /**
60818              * @event hiddenchange
60819              * Fires when a column is hidden or "unhidden".
60820              * @param {ColumnModel} this
60821              * @param {Number} columnIndex The column index
60822              * @param {Boolean} hidden true if hidden, false otherwise
60823              */
60824             "hiddenchange": true,
60825             /**
60826          * @event columnmoved
60827          * Fires when a column is moved.
60828          * @param {ColumnModel} this
60829          * @param {Number} oldIndex
60830          * @param {Number} newIndex
60831          */
60832         "columnmoved" : true,
60833         /**
60834          * @event columlockchange
60835          * Fires when a column's locked state is changed
60836          * @param {ColumnModel} this
60837          * @param {Number} colIndex
60838          * @param {Boolean} locked true if locked
60839          */
60840         "columnlockchange" : true
60841     });
60842     Roo.grid.ColumnModel.superclass.constructor.call(this);
60843 };
60844 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
60845     /**
60846      * @cfg {String} header The header text to display in the Grid view.
60847      */
60848         /**
60849      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
60850      */
60851         /**
60852      * @cfg {String} smHeader Header at Bootsrap Small width
60853      */
60854         /**
60855      * @cfg {String} mdHeader Header at Bootsrap Medium width
60856      */
60857         /**
60858      * @cfg {String} lgHeader Header at Bootsrap Large width
60859      */
60860         /**
60861      * @cfg {String} xlHeader Header at Bootsrap extra Large width
60862      */
60863     /**
60864      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
60865      * {@link Roo.data.Record} definition from which to draw the column's value. If not
60866      * specified, the column's index is used as an index into the Record's data Array.
60867      */
60868     /**
60869      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
60870      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
60871      */
60872     /**
60873      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
60874      * Defaults to the value of the {@link #defaultSortable} property.
60875      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
60876      */
60877     /**
60878      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
60879      */
60880     /**
60881      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
60882      */
60883     /**
60884      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
60885      */
60886     /**
60887      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
60888      */
60889     /**
60890      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
60891      * given the cell's data value. See {@link #setRenderer}. If not specified, the
60892      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
60893      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
60894      */
60895        /**
60896      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
60897      */
60898     /**
60899      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
60900      */
60901     /**
60902      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
60903      */
60904     /**
60905      * @cfg {String} cursor (Optional)
60906      */
60907     /**
60908      * @cfg {String} tooltip (Optional)
60909      */
60910     /**
60911      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
60912      */
60913     /**
60914      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
60915      */
60916     /**
60917      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
60918      */
60919     /**
60920      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
60921      */
60922         /**
60923      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
60924      */
60925     /**
60926      * Returns the id of the column at the specified index.
60927      * @param {Number} index The column index
60928      * @return {String} the id
60929      */
60930     getColumnId : function(index){
60931         return this.config[index].id;
60932     },
60933
60934     /**
60935      * Returns the column for a specified id.
60936      * @param {String} id The column id
60937      * @return {Object} the column
60938      */
60939     getColumnById : function(id){
60940         return this.lookup[id];
60941     },
60942
60943     
60944     /**
60945      * Returns the column Object for a specified dataIndex.
60946      * @param {String} dataIndex The column dataIndex
60947      * @return {Object|Boolean} the column or false if not found
60948      */
60949     getColumnByDataIndex: function(dataIndex){
60950         var index = this.findColumnIndex(dataIndex);
60951         return index > -1 ? this.config[index] : false;
60952     },
60953     
60954     /**
60955      * Returns the index for a specified column id.
60956      * @param {String} id The column id
60957      * @return {Number} the index, or -1 if not found
60958      */
60959     getIndexById : function(id){
60960         for(var i = 0, len = this.config.length; i < len; i++){
60961             if(this.config[i].id == id){
60962                 return i;
60963             }
60964         }
60965         return -1;
60966     },
60967     
60968     /**
60969      * Returns the index for a specified column dataIndex.
60970      * @param {String} dataIndex The column dataIndex
60971      * @return {Number} the index, or -1 if not found
60972      */
60973     
60974     findColumnIndex : function(dataIndex){
60975         for(var i = 0, len = this.config.length; i < len; i++){
60976             if(this.config[i].dataIndex == dataIndex){
60977                 return i;
60978             }
60979         }
60980         return -1;
60981     },
60982     
60983     
60984     moveColumn : function(oldIndex, newIndex){
60985         var c = this.config[oldIndex];
60986         this.config.splice(oldIndex, 1);
60987         this.config.splice(newIndex, 0, c);
60988         this.dataMap = null;
60989         this.fireEvent("columnmoved", this, oldIndex, newIndex);
60990     },
60991
60992     isLocked : function(colIndex){
60993         return this.config[colIndex].locked === true;
60994     },
60995
60996     setLocked : function(colIndex, value, suppressEvent){
60997         if(this.isLocked(colIndex) == value){
60998             return;
60999         }
61000         this.config[colIndex].locked = value;
61001         if(!suppressEvent){
61002             this.fireEvent("columnlockchange", this, colIndex, value);
61003         }
61004     },
61005
61006     getTotalLockedWidth : function(){
61007         var totalWidth = 0;
61008         for(var i = 0; i < this.config.length; i++){
61009             if(this.isLocked(i) && !this.isHidden(i)){
61010                 this.totalWidth += this.getColumnWidth(i);
61011             }
61012         }
61013         return totalWidth;
61014     },
61015
61016     getLockedCount : function(){
61017         for(var i = 0, len = this.config.length; i < len; i++){
61018             if(!this.isLocked(i)){
61019                 return i;
61020             }
61021         }
61022         
61023         return this.config.length;
61024     },
61025
61026     /**
61027      * Returns the number of columns.
61028      * @return {Number}
61029      */
61030     getColumnCount : function(visibleOnly){
61031         if(visibleOnly === true){
61032             var c = 0;
61033             for(var i = 0, len = this.config.length; i < len; i++){
61034                 if(!this.isHidden(i)){
61035                     c++;
61036                 }
61037             }
61038             return c;
61039         }
61040         return this.config.length;
61041     },
61042
61043     /**
61044      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
61045      * @param {Function} fn
61046      * @param {Object} scope (optional)
61047      * @return {Array} result
61048      */
61049     getColumnsBy : function(fn, scope){
61050         var r = [];
61051         for(var i = 0, len = this.config.length; i < len; i++){
61052             var c = this.config[i];
61053             if(fn.call(scope||this, c, i) === true){
61054                 r[r.length] = c;
61055             }
61056         }
61057         return r;
61058     },
61059
61060     /**
61061      * Returns true if the specified column is sortable.
61062      * @param {Number} col The column index
61063      * @return {Boolean}
61064      */
61065     isSortable : function(col){
61066         if(typeof this.config[col].sortable == "undefined"){
61067             return this.defaultSortable;
61068         }
61069         return this.config[col].sortable;
61070     },
61071
61072     /**
61073      * Returns the rendering (formatting) function defined for the column.
61074      * @param {Number} col The column index.
61075      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
61076      */
61077     getRenderer : function(col){
61078         if(!this.config[col].renderer){
61079             return Roo.grid.ColumnModel.defaultRenderer;
61080         }
61081         return this.config[col].renderer;
61082     },
61083
61084     /**
61085      * Sets the rendering (formatting) function for a column.
61086      * @param {Number} col The column index
61087      * @param {Function} fn The function to use to process the cell's raw data
61088      * to return HTML markup for the grid view. The render function is called with
61089      * the following parameters:<ul>
61090      * <li>Data value.</li>
61091      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
61092      * <li>css A CSS style string to apply to the table cell.</li>
61093      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
61094      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
61095      * <li>Row index</li>
61096      * <li>Column index</li>
61097      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
61098      */
61099     setRenderer : function(col, fn){
61100         this.config[col].renderer = fn;
61101     },
61102
61103     /**
61104      * Returns the width for the specified column.
61105      * @param {Number} col The column index
61106      * @param (optional) {String} gridSize bootstrap width size.
61107      * @return {Number}
61108      */
61109     getColumnWidth : function(col, gridSize)
61110         {
61111                 var cfg = this.config[col];
61112                 
61113                 if (typeof(gridSize) == 'undefined') {
61114                         return cfg.width * 1 || this.defaultWidth;
61115                 }
61116                 if (gridSize === false) { // if we set it..
61117                         return cfg.width || false;
61118                 }
61119                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
61120                 
61121                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
61122                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
61123                                 continue;
61124                         }
61125                         return cfg[ sizes[i] ];
61126                 }
61127                 return 1;
61128                 
61129     },
61130
61131     /**
61132      * Sets the width for a column.
61133      * @param {Number} col The column index
61134      * @param {Number} width The new width
61135      */
61136     setColumnWidth : function(col, width, suppressEvent){
61137         this.config[col].width = width;
61138         this.totalWidth = null;
61139         if(!suppressEvent){
61140              this.fireEvent("widthchange", this, col, width);
61141         }
61142     },
61143
61144     /**
61145      * Returns the total width of all columns.
61146      * @param {Boolean} includeHidden True to include hidden column widths
61147      * @return {Number}
61148      */
61149     getTotalWidth : function(includeHidden){
61150         if(!this.totalWidth){
61151             this.totalWidth = 0;
61152             for(var i = 0, len = this.config.length; i < len; i++){
61153                 if(includeHidden || !this.isHidden(i)){
61154                     this.totalWidth += this.getColumnWidth(i);
61155                 }
61156             }
61157         }
61158         return this.totalWidth;
61159     },
61160
61161     /**
61162      * Returns the header for the specified column.
61163      * @param {Number} col The column index
61164      * @return {String}
61165      */
61166     getColumnHeader : function(col){
61167         return this.config[col].header;
61168     },
61169
61170     /**
61171      * Sets the header for a column.
61172      * @param {Number} col The column index
61173      * @param {String} header The new header
61174      */
61175     setColumnHeader : function(col, header){
61176         this.config[col].header = header;
61177         this.fireEvent("headerchange", this, col, header);
61178     },
61179
61180     /**
61181      * Returns the tooltip for the specified column.
61182      * @param {Number} col The column index
61183      * @return {String}
61184      */
61185     getColumnTooltip : function(col){
61186             return this.config[col].tooltip;
61187     },
61188     /**
61189      * Sets the tooltip for a column.
61190      * @param {Number} col The column index
61191      * @param {String} tooltip The new tooltip
61192      */
61193     setColumnTooltip : function(col, tooltip){
61194             this.config[col].tooltip = tooltip;
61195     },
61196
61197     /**
61198      * Returns the dataIndex for the specified column.
61199      * @param {Number} col The column index
61200      * @return {Number}
61201      */
61202     getDataIndex : function(col){
61203         return this.config[col].dataIndex;
61204     },
61205
61206     /**
61207      * Sets the dataIndex for a column.
61208      * @param {Number} col The column index
61209      * @param {Number} dataIndex The new dataIndex
61210      */
61211     setDataIndex : function(col, dataIndex){
61212         this.config[col].dataIndex = dataIndex;
61213     },
61214
61215     
61216     
61217     /**
61218      * Returns true if the cell is editable.
61219      * @param {Number} colIndex The column index
61220      * @param {Number} rowIndex The row index - this is nto actually used..?
61221      * @return {Boolean}
61222      */
61223     isCellEditable : function(colIndex, rowIndex){
61224         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
61225     },
61226
61227     /**
61228      * Returns the editor defined for the cell/column.
61229      * return false or null to disable editing.
61230      * @param {Number} colIndex The column index
61231      * @param {Number} rowIndex The row index
61232      * @return {Object}
61233      */
61234     getCellEditor : function(colIndex, rowIndex){
61235         return this.config[colIndex].editor;
61236     },
61237
61238     /**
61239      * Sets if a column is editable.
61240      * @param {Number} col The column index
61241      * @param {Boolean} editable True if the column is editable
61242      */
61243     setEditable : function(col, editable){
61244         this.config[col].editable = editable;
61245     },
61246
61247
61248     /**
61249      * Returns true if the column is hidden.
61250      * @param {Number} colIndex The column index
61251      * @return {Boolean}
61252      */
61253     isHidden : function(colIndex){
61254         return this.config[colIndex].hidden;
61255     },
61256
61257
61258     /**
61259      * Returns true if the column width cannot be changed
61260      */
61261     isFixed : function(colIndex){
61262         return this.config[colIndex].fixed;
61263     },
61264
61265     /**
61266      * Returns true if the column can be resized
61267      * @return {Boolean}
61268      */
61269     isResizable : function(colIndex){
61270         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
61271     },
61272     /**
61273      * Sets if a column is hidden.
61274      * @param {Number} colIndex The column index
61275      * @param {Boolean} hidden True if the column is hidden
61276      */
61277     setHidden : function(colIndex, hidden){
61278         this.config[colIndex].hidden = hidden;
61279         this.totalWidth = null;
61280         this.fireEvent("hiddenchange", this, colIndex, hidden);
61281     },
61282
61283     /**
61284      * Sets the editor for a column.
61285      * @param {Number} col The column index
61286      * @param {Object} editor The editor object
61287      */
61288     setEditor : function(col, editor){
61289         this.config[col].editor = editor;
61290     },
61291     /**
61292      * Add a column (experimental...) - defaults to adding to the end..
61293      * @param {Object} config 
61294     */
61295     addColumn : function(c)
61296     {
61297     
61298         var i = this.config.length;
61299         this.config[i] = c;
61300         
61301         if(typeof c.dataIndex == "undefined"){
61302             c.dataIndex = i;
61303         }
61304         if(typeof c.renderer == "string"){
61305             c.renderer = Roo.util.Format[c.renderer];
61306         }
61307         if(typeof c.id == "undefined"){
61308             c.id = Roo.id();
61309         }
61310         if(c.editor && c.editor.xtype){
61311             c.editor  = Roo.factory(c.editor, Roo.grid);
61312         }
61313         if(c.editor && c.editor.isFormField){
61314             c.editor = new Roo.grid.GridEditor(c.editor);
61315         }
61316         this.lookup[c.id] = c;
61317     }
61318     
61319 });
61320
61321 Roo.grid.ColumnModel.defaultRenderer = function(value)
61322 {
61323     if(typeof value == "object") {
61324         return value;
61325     }
61326         if(typeof value == "string" && value.length < 1){
61327             return "&#160;";
61328         }
61329     
61330         return String.format("{0}", value);
61331 };
61332
61333 // Alias for backwards compatibility
61334 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
61335 /*
61336  * Based on:
61337  * Ext JS Library 1.1.1
61338  * Copyright(c) 2006-2007, Ext JS, LLC.
61339  *
61340  * Originally Released Under LGPL - original licence link has changed is not relivant.
61341  *
61342  * Fork - LGPL
61343  * <script type="text/javascript">
61344  */
61345
61346 /**
61347  * @class Roo.grid.AbstractSelectionModel
61348  * @extends Roo.util.Observable
61349  * @abstract
61350  * Abstract base class for grid SelectionModels.  It provides the interface that should be
61351  * implemented by descendant classes.  This class should not be directly instantiated.
61352  * @constructor
61353  */
61354 Roo.grid.AbstractSelectionModel = function(){
61355     this.locked = false;
61356     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
61357 };
61358
61359 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
61360     /** @ignore Called by the grid automatically. Do not call directly. */
61361     init : function(grid){
61362         this.grid = grid;
61363         this.initEvents();
61364     },
61365
61366     /**
61367      * Locks the selections.
61368      */
61369     lock : function(){
61370         this.locked = true;
61371     },
61372
61373     /**
61374      * Unlocks the selections.
61375      */
61376     unlock : function(){
61377         this.locked = false;
61378     },
61379
61380     /**
61381      * Returns true if the selections are locked.
61382      * @return {Boolean}
61383      */
61384     isLocked : function(){
61385         return this.locked;
61386     }
61387 });/*
61388  * Based on:
61389  * Ext JS Library 1.1.1
61390  * Copyright(c) 2006-2007, Ext JS, LLC.
61391  *
61392  * Originally Released Under LGPL - original licence link has changed is not relivant.
61393  *
61394  * Fork - LGPL
61395  * <script type="text/javascript">
61396  */
61397 /**
61398  * @extends Roo.grid.AbstractSelectionModel
61399  * @class Roo.grid.RowSelectionModel
61400  * The default SelectionModel used by {@link Roo.grid.Grid}.
61401  * It supports multiple selections and keyboard selection/navigation. 
61402  * @constructor
61403  * @param {Object} config
61404  */
61405 Roo.grid.RowSelectionModel = function(config){
61406     Roo.apply(this, config);
61407     this.selections = new Roo.util.MixedCollection(false, function(o){
61408         return o.id;
61409     });
61410
61411     this.last = false;
61412     this.lastActive = false;
61413
61414     this.addEvents({
61415         /**
61416         * @event selectionchange
61417         * Fires when the selection changes
61418         * @param {SelectionModel} this
61419         */
61420        "selectionchange" : true,
61421        /**
61422         * @event afterselectionchange
61423         * Fires after the selection changes (eg. by key press or clicking)
61424         * @param {SelectionModel} this
61425         */
61426        "afterselectionchange" : true,
61427        /**
61428         * @event beforerowselect
61429         * Fires when a row is selected being selected, return false to cancel.
61430         * @param {SelectionModel} this
61431         * @param {Number} rowIndex The selected index
61432         * @param {Boolean} keepExisting False if other selections will be cleared
61433         */
61434        "beforerowselect" : true,
61435        /**
61436         * @event rowselect
61437         * Fires when a row is selected.
61438         * @param {SelectionModel} this
61439         * @param {Number} rowIndex The selected index
61440         * @param {Roo.data.Record} r The record
61441         */
61442        "rowselect" : true,
61443        /**
61444         * @event rowdeselect
61445         * Fires when a row is deselected.
61446         * @param {SelectionModel} this
61447         * @param {Number} rowIndex The selected index
61448         */
61449         "rowdeselect" : true
61450     });
61451     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
61452     this.locked = false;
61453 };
61454
61455 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
61456     /**
61457      * @cfg {Boolean} singleSelect
61458      * True to allow selection of only one row at a time (defaults to false)
61459      */
61460     singleSelect : false,
61461
61462     // private
61463     initEvents : function(){
61464
61465         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
61466             this.grid.on("mousedown", this.handleMouseDown, this);
61467         }else{ // allow click to work like normal
61468             this.grid.on("rowclick", this.handleDragableRowClick, this);
61469         }
61470         // bootstrap does not have a view..
61471         var view = this.grid.view ? this.grid.view : this.grid;
61472         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
61473             "up" : function(e){
61474                 if(!e.shiftKey){
61475                     this.selectPrevious(e.shiftKey);
61476                 }else if(this.last !== false && this.lastActive !== false){
61477                     var last = this.last;
61478                     this.selectRange(this.last,  this.lastActive-1);
61479                     view.focusRow(this.lastActive);
61480                     if(last !== false){
61481                         this.last = last;
61482                     }
61483                 }else{
61484                     this.selectFirstRow();
61485                 }
61486                 this.fireEvent("afterselectionchange", this);
61487             },
61488             "down" : function(e){
61489                 if(!e.shiftKey){
61490                     this.selectNext(e.shiftKey);
61491                 }else if(this.last !== false && this.lastActive !== false){
61492                     var last = this.last;
61493                     this.selectRange(this.last,  this.lastActive+1);
61494                     view.focusRow(this.lastActive);
61495                     if(last !== false){
61496                         this.last = last;
61497                     }
61498                 }else{
61499                     this.selectFirstRow();
61500                 }
61501                 this.fireEvent("afterselectionchange", this);
61502             },
61503             scope: this
61504         });
61505
61506          
61507         view.on("refresh", this.onRefresh, this);
61508         view.on("rowupdated", this.onRowUpdated, this);
61509         view.on("rowremoved", this.onRemove, this);
61510     },
61511
61512     // private
61513     onRefresh : function(){
61514         var ds = this.grid.ds, i, v = this.grid.view;
61515         var s = this.selections;
61516         s.each(function(r){
61517             if((i = ds.indexOfId(r.id)) != -1){
61518                 v.onRowSelect(i);
61519                 s.add(ds.getAt(i)); // updating the selection relate data
61520             }else{
61521                 s.remove(r);
61522             }
61523         });
61524     },
61525
61526     // private
61527     onRemove : function(v, index, r){
61528         this.selections.remove(r);
61529     },
61530
61531     // private
61532     onRowUpdated : function(v, index, r){
61533         if(this.isSelected(r)){
61534             v.onRowSelect(index);
61535         }
61536     },
61537
61538     /**
61539      * Select records.
61540      * @param {Array} records The records to select
61541      * @param {Boolean} keepExisting (optional) True to keep existing selections
61542      */
61543     selectRecords : function(records, keepExisting){
61544         if(!keepExisting){
61545             this.clearSelections();
61546         }
61547         var ds = this.grid.ds;
61548         for(var i = 0, len = records.length; i < len; i++){
61549             this.selectRow(ds.indexOf(records[i]), true);
61550         }
61551     },
61552
61553     /**
61554      * Gets the number of selected rows.
61555      * @return {Number}
61556      */
61557     getCount : function(){
61558         return this.selections.length;
61559     },
61560
61561     /**
61562      * Selects the first row in the grid.
61563      */
61564     selectFirstRow : function(){
61565         this.selectRow(0);
61566     },
61567
61568     /**
61569      * Select the last row.
61570      * @param {Boolean} keepExisting (optional) True to keep existing selections
61571      */
61572     selectLastRow : function(keepExisting){
61573         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
61574     },
61575
61576     /**
61577      * Selects the row immediately following the last selected row.
61578      * @param {Boolean} keepExisting (optional) True to keep existing selections
61579      */
61580     selectNext : function(keepExisting){
61581         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
61582             this.selectRow(this.last+1, keepExisting);
61583             var view = this.grid.view ? this.grid.view : this.grid;
61584             view.focusRow(this.last);
61585         }
61586     },
61587
61588     /**
61589      * Selects the row that precedes the last selected row.
61590      * @param {Boolean} keepExisting (optional) True to keep existing selections
61591      */
61592     selectPrevious : function(keepExisting){
61593         if(this.last){
61594             this.selectRow(this.last-1, keepExisting);
61595             var view = this.grid.view ? this.grid.view : this.grid;
61596             view.focusRow(this.last);
61597         }
61598     },
61599
61600     /**
61601      * Returns the selected records
61602      * @return {Array} Array of selected records
61603      */
61604     getSelections : function(){
61605         return [].concat(this.selections.items);
61606     },
61607
61608     /**
61609      * Returns the first selected record.
61610      * @return {Record}
61611      */
61612     getSelected : function(){
61613         return this.selections.itemAt(0);
61614     },
61615
61616
61617     /**
61618      * Clears all selections.
61619      */
61620     clearSelections : function(fast){
61621         if(this.locked) {
61622             return;
61623         }
61624         if(fast !== true){
61625             var ds = this.grid.ds;
61626             var s = this.selections;
61627             s.each(function(r){
61628                 this.deselectRow(ds.indexOfId(r.id));
61629             }, this);
61630             s.clear();
61631         }else{
61632             this.selections.clear();
61633         }
61634         this.last = false;
61635     },
61636
61637
61638     /**
61639      * Selects all rows.
61640      */
61641     selectAll : function(){
61642         if(this.locked) {
61643             return;
61644         }
61645         this.selections.clear();
61646         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
61647             this.selectRow(i, true);
61648         }
61649     },
61650
61651     /**
61652      * Returns True if there is a selection.
61653      * @return {Boolean}
61654      */
61655     hasSelection : function(){
61656         return this.selections.length > 0;
61657     },
61658
61659     /**
61660      * Returns True if the specified row is selected.
61661      * @param {Number/Record} record The record or index of the record to check
61662      * @return {Boolean}
61663      */
61664     isSelected : function(index){
61665         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
61666         return (r && this.selections.key(r.id) ? true : false);
61667     },
61668
61669     /**
61670      * Returns True if the specified record id is selected.
61671      * @param {String} id The id of record to check
61672      * @return {Boolean}
61673      */
61674     isIdSelected : function(id){
61675         return (this.selections.key(id) ? true : false);
61676     },
61677
61678     // private
61679     handleMouseDown : function(e, t)
61680     {
61681         var view = this.grid.view ? this.grid.view : this.grid;
61682         var rowIndex;
61683         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
61684             return;
61685         };
61686         if(e.shiftKey && this.last !== false){
61687             var last = this.last;
61688             this.selectRange(last, rowIndex, e.ctrlKey);
61689             this.last = last; // reset the last
61690             view.focusRow(rowIndex);
61691         }else{
61692             var isSelected = this.isSelected(rowIndex);
61693             if(e.button !== 0 && isSelected){
61694                 view.focusRow(rowIndex);
61695             }else if(e.ctrlKey && isSelected){
61696                 this.deselectRow(rowIndex);
61697             }else if(!isSelected){
61698                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
61699                 view.focusRow(rowIndex);
61700             }
61701         }
61702         this.fireEvent("afterselectionchange", this);
61703     },
61704     // private
61705     handleDragableRowClick :  function(grid, rowIndex, e) 
61706     {
61707         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
61708             this.selectRow(rowIndex, false);
61709             var view = this.grid.view ? this.grid.view : this.grid;
61710             view.focusRow(rowIndex);
61711              this.fireEvent("afterselectionchange", this);
61712         }
61713     },
61714     
61715     /**
61716      * Selects multiple rows.
61717      * @param {Array} rows Array of the indexes of the row to select
61718      * @param {Boolean} keepExisting (optional) True to keep existing selections
61719      */
61720     selectRows : function(rows, keepExisting){
61721         if(!keepExisting){
61722             this.clearSelections();
61723         }
61724         for(var i = 0, len = rows.length; i < len; i++){
61725             this.selectRow(rows[i], true);
61726         }
61727     },
61728
61729     /**
61730      * Selects a range of rows. All rows in between startRow and endRow are also selected.
61731      * @param {Number} startRow The index of the first row in the range
61732      * @param {Number} endRow The index of the last row in the range
61733      * @param {Boolean} keepExisting (optional) True to retain existing selections
61734      */
61735     selectRange : function(startRow, endRow, keepExisting){
61736         if(this.locked) {
61737             return;
61738         }
61739         if(!keepExisting){
61740             this.clearSelections();
61741         }
61742         if(startRow <= endRow){
61743             for(var i = startRow; i <= endRow; i++){
61744                 this.selectRow(i, true);
61745             }
61746         }else{
61747             for(var i = startRow; i >= endRow; i--){
61748                 this.selectRow(i, true);
61749             }
61750         }
61751     },
61752
61753     /**
61754      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
61755      * @param {Number} startRow The index of the first row in the range
61756      * @param {Number} endRow The index of the last row in the range
61757      */
61758     deselectRange : function(startRow, endRow, preventViewNotify){
61759         if(this.locked) {
61760             return;
61761         }
61762         for(var i = startRow; i <= endRow; i++){
61763             this.deselectRow(i, preventViewNotify);
61764         }
61765     },
61766
61767     /**
61768      * Selects a row.
61769      * @param {Number} row The index of the row to select
61770      * @param {Boolean} keepExisting (optional) True to keep existing selections
61771      */
61772     selectRow : function(index, keepExisting, preventViewNotify){
61773         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
61774             return;
61775         }
61776         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
61777             if(!keepExisting || this.singleSelect){
61778                 this.clearSelections();
61779             }
61780             var r = this.grid.ds.getAt(index);
61781             this.selections.add(r);
61782             this.last = this.lastActive = index;
61783             if(!preventViewNotify){
61784                 var view = this.grid.view ? this.grid.view : this.grid;
61785                 view.onRowSelect(index);
61786             }
61787             this.fireEvent("rowselect", this, index, r);
61788             this.fireEvent("selectionchange", this);
61789         }
61790     },
61791
61792     /**
61793      * Deselects a row.
61794      * @param {Number} row The index of the row to deselect
61795      */
61796     deselectRow : function(index, preventViewNotify){
61797         if(this.locked) {
61798             return;
61799         }
61800         if(this.last == index){
61801             this.last = false;
61802         }
61803         if(this.lastActive == index){
61804             this.lastActive = false;
61805         }
61806         var r = this.grid.ds.getAt(index);
61807         this.selections.remove(r);
61808         if(!preventViewNotify){
61809             var view = this.grid.view ? this.grid.view : this.grid;
61810             view.onRowDeselect(index);
61811         }
61812         this.fireEvent("rowdeselect", this, index);
61813         this.fireEvent("selectionchange", this);
61814     },
61815
61816     // private
61817     restoreLast : function(){
61818         if(this._last){
61819             this.last = this._last;
61820         }
61821     },
61822
61823     // private
61824     acceptsNav : function(row, col, cm){
61825         return !cm.isHidden(col) && cm.isCellEditable(col, row);
61826     },
61827
61828     // private
61829     onEditorKey : function(field, e){
61830         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
61831         if(k == e.TAB){
61832             e.stopEvent();
61833             ed.completeEdit();
61834             if(e.shiftKey){
61835                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
61836             }else{
61837                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
61838             }
61839         }else if(k == e.ENTER && !e.ctrlKey){
61840             e.stopEvent();
61841             ed.completeEdit();
61842             if(e.shiftKey){
61843                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
61844             }else{
61845                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
61846             }
61847         }else if(k == e.ESC){
61848             ed.cancelEdit();
61849         }
61850         if(newCell){
61851             g.startEditing(newCell[0], newCell[1]);
61852         }
61853     }
61854 });/*
61855  * Based on:
61856  * Ext JS Library 1.1.1
61857  * Copyright(c) 2006-2007, Ext JS, LLC.
61858  *
61859  * Originally Released Under LGPL - original licence link has changed is not relivant.
61860  *
61861  * Fork - LGPL
61862  * <script type="text/javascript">
61863  */
61864 /**
61865  * @class Roo.grid.CellSelectionModel
61866  * @extends Roo.grid.AbstractSelectionModel
61867  * This class provides the basic implementation for cell selection in a grid.
61868  * @constructor
61869  * @param {Object} config The object containing the configuration of this model.
61870  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
61871  */
61872 Roo.grid.CellSelectionModel = function(config){
61873     Roo.apply(this, config);
61874
61875     this.selection = null;
61876
61877     this.addEvents({
61878         /**
61879              * @event beforerowselect
61880              * Fires before a cell is selected.
61881              * @param {SelectionModel} this
61882              * @param {Number} rowIndex The selected row index
61883              * @param {Number} colIndex The selected cell index
61884              */
61885             "beforecellselect" : true,
61886         /**
61887              * @event cellselect
61888              * Fires when a cell is selected.
61889              * @param {SelectionModel} this
61890              * @param {Number} rowIndex The selected row index
61891              * @param {Number} colIndex The selected cell index
61892              */
61893             "cellselect" : true,
61894         /**
61895              * @event selectionchange
61896              * Fires when the active selection changes.
61897              * @param {SelectionModel} this
61898              * @param {Object} selection null for no selection or an object (o) with two properties
61899                 <ul>
61900                 <li>o.record: the record object for the row the selection is in</li>
61901                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
61902                 </ul>
61903              */
61904             "selectionchange" : true,
61905         /**
61906              * @event tabend
61907              * Fires when the tab (or enter) was pressed on the last editable cell
61908              * You can use this to trigger add new row.
61909              * @param {SelectionModel} this
61910              */
61911             "tabend" : true,
61912          /**
61913              * @event beforeeditnext
61914              * Fires before the next editable sell is made active
61915              * You can use this to skip to another cell or fire the tabend
61916              *    if you set cell to false
61917              * @param {Object} eventdata object : { cell : [ row, col ] } 
61918              */
61919             "beforeeditnext" : true
61920     });
61921     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
61922 };
61923
61924 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
61925     
61926     enter_is_tab: false,
61927
61928     /** @ignore */
61929     initEvents : function(){
61930         this.grid.on("mousedown", this.handleMouseDown, this);
61931         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
61932         var view = this.grid.view;
61933         view.on("refresh", this.onViewChange, this);
61934         view.on("rowupdated", this.onRowUpdated, this);
61935         view.on("beforerowremoved", this.clearSelections, this);
61936         view.on("beforerowsinserted", this.clearSelections, this);
61937         if(this.grid.isEditor){
61938             this.grid.on("beforeedit", this.beforeEdit,  this);
61939         }
61940     },
61941
61942         //private
61943     beforeEdit : function(e){
61944         this.select(e.row, e.column, false, true, e.record);
61945     },
61946
61947         //private
61948     onRowUpdated : function(v, index, r){
61949         if(this.selection && this.selection.record == r){
61950             v.onCellSelect(index, this.selection.cell[1]);
61951         }
61952     },
61953
61954         //private
61955     onViewChange : function(){
61956         this.clearSelections(true);
61957     },
61958
61959         /**
61960          * Returns the currently selected cell,.
61961          * @return {Array} The selected cell (row, column) or null if none selected.
61962          */
61963     getSelectedCell : function(){
61964         return this.selection ? this.selection.cell : null;
61965     },
61966
61967     /**
61968      * Clears all selections.
61969      * @param {Boolean} true to prevent the gridview from being notified about the change.
61970      */
61971     clearSelections : function(preventNotify){
61972         var s = this.selection;
61973         if(s){
61974             if(preventNotify !== true){
61975                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
61976             }
61977             this.selection = null;
61978             this.fireEvent("selectionchange", this, null);
61979         }
61980     },
61981
61982     /**
61983      * Returns true if there is a selection.
61984      * @return {Boolean}
61985      */
61986     hasSelection : function(){
61987         return this.selection ? true : false;
61988     },
61989
61990     /** @ignore */
61991     handleMouseDown : function(e, t){
61992         var v = this.grid.getView();
61993         if(this.isLocked()){
61994             return;
61995         };
61996         var row = v.findRowIndex(t);
61997         var cell = v.findCellIndex(t);
61998         if(row !== false && cell !== false){
61999             this.select(row, cell);
62000         }
62001     },
62002
62003     /**
62004      * Selects a cell.
62005      * @param {Number} rowIndex
62006      * @param {Number} collIndex
62007      */
62008     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
62009         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
62010             this.clearSelections();
62011             r = r || this.grid.dataSource.getAt(rowIndex);
62012             this.selection = {
62013                 record : r,
62014                 cell : [rowIndex, colIndex]
62015             };
62016             if(!preventViewNotify){
62017                 var v = this.grid.getView();
62018                 v.onCellSelect(rowIndex, colIndex);
62019                 if(preventFocus !== true){
62020                     v.focusCell(rowIndex, colIndex);
62021                 }
62022             }
62023             this.fireEvent("cellselect", this, rowIndex, colIndex);
62024             this.fireEvent("selectionchange", this, this.selection);
62025         }
62026     },
62027
62028         //private
62029     isSelectable : function(rowIndex, colIndex, cm){
62030         return !cm.isHidden(colIndex);
62031     },
62032
62033     /** @ignore */
62034     handleKeyDown : function(e){
62035         //Roo.log('Cell Sel Model handleKeyDown');
62036         if(!e.isNavKeyPress()){
62037             return;
62038         }
62039         var g = this.grid, s = this.selection;
62040         if(!s){
62041             e.stopEvent();
62042             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
62043             if(cell){
62044                 this.select(cell[0], cell[1]);
62045             }
62046             return;
62047         }
62048         var sm = this;
62049         var walk = function(row, col, step){
62050             return g.walkCells(row, col, step, sm.isSelectable,  sm);
62051         };
62052         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
62053         var newCell;
62054
62055       
62056
62057         switch(k){
62058             case e.TAB:
62059                 // handled by onEditorKey
62060                 if (g.isEditor && g.editing) {
62061                     return;
62062                 }
62063                 if(e.shiftKey) {
62064                     newCell = walk(r, c-1, -1);
62065                 } else {
62066                     newCell = walk(r, c+1, 1);
62067                 }
62068                 break;
62069             
62070             case e.DOWN:
62071                newCell = walk(r+1, c, 1);
62072                 break;
62073             
62074             case e.UP:
62075                 newCell = walk(r-1, c, -1);
62076                 break;
62077             
62078             case e.RIGHT:
62079                 newCell = walk(r, c+1, 1);
62080                 break;
62081             
62082             case e.LEFT:
62083                 newCell = walk(r, c-1, -1);
62084                 break;
62085             
62086             case e.ENTER:
62087                 
62088                 if(g.isEditor && !g.editing){
62089                    g.startEditing(r, c);
62090                    e.stopEvent();
62091                    return;
62092                 }
62093                 
62094                 
62095              break;
62096         };
62097         if(newCell){
62098             this.select(newCell[0], newCell[1]);
62099             e.stopEvent();
62100             
62101         }
62102     },
62103
62104     acceptsNav : function(row, col, cm){
62105         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62106     },
62107     /**
62108      * Selects a cell.
62109      * @param {Number} field (not used) - as it's normally used as a listener
62110      * @param {Number} e - event - fake it by using
62111      *
62112      * var e = Roo.EventObjectImpl.prototype;
62113      * e.keyCode = e.TAB
62114      *
62115      * 
62116      */
62117     onEditorKey : function(field, e){
62118         
62119         var k = e.getKey(),
62120             newCell,
62121             g = this.grid,
62122             ed = g.activeEditor,
62123             forward = false;
62124         ///Roo.log('onEditorKey' + k);
62125         
62126         
62127         if (this.enter_is_tab && k == e.ENTER) {
62128             k = e.TAB;
62129         }
62130         
62131         if(k == e.TAB){
62132             if(e.shiftKey){
62133                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62134             }else{
62135                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62136                 forward = true;
62137             }
62138             
62139             e.stopEvent();
62140             
62141         } else if(k == e.ENTER &&  !e.ctrlKey){
62142             ed.completeEdit();
62143             e.stopEvent();
62144             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62145         
62146                 } else if(k == e.ESC){
62147             ed.cancelEdit();
62148         }
62149                 
62150         if (newCell) {
62151             var ecall = { cell : newCell, forward : forward };
62152             this.fireEvent('beforeeditnext', ecall );
62153             newCell = ecall.cell;
62154                         forward = ecall.forward;
62155         }
62156                 
62157         if(newCell){
62158             //Roo.log('next cell after edit');
62159             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
62160         } else if (forward) {
62161             // tabbed past last
62162             this.fireEvent.defer(100, this, ['tabend',this]);
62163         }
62164     }
62165 });/*
62166  * Based on:
62167  * Ext JS Library 1.1.1
62168  * Copyright(c) 2006-2007, Ext JS, LLC.
62169  *
62170  * Originally Released Under LGPL - original licence link has changed is not relivant.
62171  *
62172  * Fork - LGPL
62173  * <script type="text/javascript">
62174  */
62175  
62176 /**
62177  * @class Roo.grid.EditorGrid
62178  * @extends Roo.grid.Grid
62179  * Class for creating and editable grid.
62180  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
62181  * The container MUST have some type of size defined for the grid to fill. The container will be 
62182  * automatically set to position relative if it isn't already.
62183  * @param {Object} dataSource The data model to bind to
62184  * @param {Object} colModel The column model with info about this grid's columns
62185  */
62186 Roo.grid.EditorGrid = function(container, config){
62187     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
62188     this.getGridEl().addClass("xedit-grid");
62189
62190     if(!this.selModel){
62191         this.selModel = new Roo.grid.CellSelectionModel();
62192     }
62193
62194     this.activeEditor = null;
62195
62196         this.addEvents({
62197             /**
62198              * @event beforeedit
62199              * Fires before cell editing is triggered. The edit event object has the following properties <br />
62200              * <ul style="padding:5px;padding-left:16px;">
62201              * <li>grid - This grid</li>
62202              * <li>record - The record being edited</li>
62203              * <li>field - The field name being edited</li>
62204              * <li>value - The value for the field being edited.</li>
62205              * <li>row - The grid row index</li>
62206              * <li>column - The grid column index</li>
62207              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62208              * </ul>
62209              * @param {Object} e An edit event (see above for description)
62210              */
62211             "beforeedit" : true,
62212             /**
62213              * @event afteredit
62214              * Fires after a cell is edited. <br />
62215              * <ul style="padding:5px;padding-left:16px;">
62216              * <li>grid - This grid</li>
62217              * <li>record - The record being edited</li>
62218              * <li>field - The field name being edited</li>
62219              * <li>value - The value being set</li>
62220              * <li>originalValue - The original value for the field, before the edit.</li>
62221              * <li>row - The grid row index</li>
62222              * <li>column - The grid column index</li>
62223              * </ul>
62224              * @param {Object} e An edit event (see above for description)
62225              */
62226             "afteredit" : true,
62227             /**
62228              * @event validateedit
62229              * Fires after a cell is edited, but before the value is set in the record. 
62230          * You can use this to modify the value being set in the field, Return false
62231              * to cancel the change. The edit event object has the following properties <br />
62232              * <ul style="padding:5px;padding-left:16px;">
62233          * <li>editor - This editor</li>
62234              * <li>grid - This grid</li>
62235              * <li>record - The record being edited</li>
62236              * <li>field - The field name being edited</li>
62237              * <li>value - The value being set</li>
62238              * <li>originalValue - The original value for the field, before the edit.</li>
62239              * <li>row - The grid row index</li>
62240              * <li>column - The grid column index</li>
62241              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62242              * </ul>
62243              * @param {Object} e An edit event (see above for description)
62244              */
62245             "validateedit" : true
62246         });
62247     this.on("bodyscroll", this.stopEditing,  this);
62248     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
62249 };
62250
62251 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
62252     /**
62253      * @cfg {Number} clicksToEdit
62254      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
62255      */
62256     clicksToEdit: 2,
62257
62258     // private
62259     isEditor : true,
62260     // private
62261     trackMouseOver: false, // causes very odd FF errors
62262
62263     onCellDblClick : function(g, row, col){
62264         this.startEditing(row, col);
62265     },
62266
62267     onEditComplete : function(ed, value, startValue){
62268         this.editing = false;
62269         this.activeEditor = null;
62270         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
62271         var r = ed.record;
62272         var field = this.colModel.getDataIndex(ed.col);
62273         var e = {
62274             grid: this,
62275             record: r,
62276             field: field,
62277             originalValue: startValue,
62278             value: value,
62279             row: ed.row,
62280             column: ed.col,
62281             cancel:false,
62282             editor: ed
62283         };
62284         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
62285         cell.show();
62286           
62287         if(String(value) !== String(startValue)){
62288             
62289             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
62290                 r.set(field, e.value);
62291                 // if we are dealing with a combo box..
62292                 // then we also set the 'name' colum to be the displayField
62293                 if (ed.field.displayField && ed.field.name) {
62294                     r.set(ed.field.name, ed.field.el.dom.value);
62295                 }
62296                 
62297                 delete e.cancel; //?? why!!!
62298                 this.fireEvent("afteredit", e);
62299             }
62300         } else {
62301             this.fireEvent("afteredit", e); // always fire it!
62302         }
62303         this.view.focusCell(ed.row, ed.col);
62304     },
62305
62306     /**
62307      * Starts editing the specified for the specified row/column
62308      * @param {Number} rowIndex
62309      * @param {Number} colIndex
62310      */
62311     startEditing : function(row, col){
62312         this.stopEditing();
62313         if(this.colModel.isCellEditable(col, row)){
62314             this.view.ensureVisible(row, col, true);
62315           
62316             var r = this.dataSource.getAt(row);
62317             var field = this.colModel.getDataIndex(col);
62318             var cell = Roo.get(this.view.getCell(row,col));
62319             var e = {
62320                 grid: this,
62321                 record: r,
62322                 field: field,
62323                 value: r.data[field],
62324                 row: row,
62325                 column: col,
62326                 cancel:false 
62327             };
62328             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
62329                 this.editing = true;
62330                 var ed = this.colModel.getCellEditor(col, row);
62331                 
62332                 if (!ed) {
62333                     return;
62334                 }
62335                 if(!ed.rendered){
62336                     ed.render(ed.parentEl || document.body);
62337                 }
62338                 ed.field.reset();
62339                
62340                 cell.hide();
62341                 
62342                 (function(){ // complex but required for focus issues in safari, ie and opera
62343                     ed.row = row;
62344                     ed.col = col;
62345                     ed.record = r;
62346                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
62347                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
62348                     this.activeEditor = ed;
62349                     var v = r.data[field];
62350                     ed.startEdit(this.view.getCell(row, col), v);
62351                     // combo's with 'displayField and name set
62352                     if (ed.field.displayField && ed.field.name) {
62353                         ed.field.el.dom.value = r.data[ed.field.name];
62354                     }
62355                     
62356                     
62357                 }).defer(50, this);
62358             }
62359         }
62360     },
62361         
62362     /**
62363      * Stops any active editing
62364      */
62365     stopEditing : function(){
62366         if(this.activeEditor){
62367             this.activeEditor.completeEdit();
62368         }
62369         this.activeEditor = null;
62370     },
62371         
62372          /**
62373      * Called to get grid's drag proxy text, by default returns this.ddText.
62374      * @return {String}
62375      */
62376     getDragDropText : function(){
62377         var count = this.selModel.getSelectedCell() ? 1 : 0;
62378         return String.format(this.ddText, count, count == 1 ? '' : 's');
62379     }
62380         
62381 });/*
62382  * Based on:
62383  * Ext JS Library 1.1.1
62384  * Copyright(c) 2006-2007, Ext JS, LLC.
62385  *
62386  * Originally Released Under LGPL - original licence link has changed is not relivant.
62387  *
62388  * Fork - LGPL
62389  * <script type="text/javascript">
62390  */
62391
62392 // private - not really -- you end up using it !
62393 // This is a support class used internally by the Grid components
62394
62395 /**
62396  * @class Roo.grid.GridEditor
62397  * @extends Roo.Editor
62398  * Class for creating and editable grid elements.
62399  * @param {Object} config any settings (must include field)
62400  */
62401 Roo.grid.GridEditor = function(field, config){
62402     if (!config && field.field) {
62403         config = field;
62404         field = Roo.factory(config.field, Roo.form);
62405     }
62406     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
62407     field.monitorTab = false;
62408 };
62409
62410 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
62411     
62412     /**
62413      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
62414      */
62415     
62416     alignment: "tl-tl",
62417     autoSize: "width",
62418     hideEl : false,
62419     cls: "x-small-editor x-grid-editor",
62420     shim:false,
62421     shadow:"frame"
62422 });/*
62423  * Based on:
62424  * Ext JS Library 1.1.1
62425  * Copyright(c) 2006-2007, Ext JS, LLC.
62426  *
62427  * Originally Released Under LGPL - original licence link has changed is not relivant.
62428  *
62429  * Fork - LGPL
62430  * <script type="text/javascript">
62431  */
62432   
62433
62434   
62435 Roo.grid.PropertyRecord = Roo.data.Record.create([
62436     {name:'name',type:'string'},  'value'
62437 ]);
62438
62439
62440 Roo.grid.PropertyStore = function(grid, source){
62441     this.grid = grid;
62442     this.store = new Roo.data.Store({
62443         recordType : Roo.grid.PropertyRecord
62444     });
62445     this.store.on('update', this.onUpdate,  this);
62446     if(source){
62447         this.setSource(source);
62448     }
62449     Roo.grid.PropertyStore.superclass.constructor.call(this);
62450 };
62451
62452
62453
62454 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
62455     setSource : function(o){
62456         this.source = o;
62457         this.store.removeAll();
62458         var data = [];
62459         for(var k in o){
62460             if(this.isEditableValue(o[k])){
62461                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
62462             }
62463         }
62464         this.store.loadRecords({records: data}, {}, true);
62465     },
62466
62467     onUpdate : function(ds, record, type){
62468         if(type == Roo.data.Record.EDIT){
62469             var v = record.data['value'];
62470             var oldValue = record.modified['value'];
62471             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
62472                 this.source[record.id] = v;
62473                 record.commit();
62474                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
62475             }else{
62476                 record.reject();
62477             }
62478         }
62479     },
62480
62481     getProperty : function(row){
62482        return this.store.getAt(row);
62483     },
62484
62485     isEditableValue: function(val){
62486         if(val && val instanceof Date){
62487             return true;
62488         }else if(typeof val == 'object' || typeof val == 'function'){
62489             return false;
62490         }
62491         return true;
62492     },
62493
62494     setValue : function(prop, value){
62495         this.source[prop] = value;
62496         this.store.getById(prop).set('value', value);
62497     },
62498
62499     getSource : function(){
62500         return this.source;
62501     }
62502 });
62503
62504 Roo.grid.PropertyColumnModel = function(grid, store){
62505     this.grid = grid;
62506     var g = Roo.grid;
62507     g.PropertyColumnModel.superclass.constructor.call(this, [
62508         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
62509         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
62510     ]);
62511     this.store = store;
62512     this.bselect = Roo.DomHelper.append(document.body, {
62513         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
62514             {tag: 'option', value: 'true', html: 'true'},
62515             {tag: 'option', value: 'false', html: 'false'}
62516         ]
62517     });
62518     Roo.id(this.bselect);
62519     var f = Roo.form;
62520     this.editors = {
62521         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
62522         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
62523         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
62524         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
62525         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
62526     };
62527     this.renderCellDelegate = this.renderCell.createDelegate(this);
62528     this.renderPropDelegate = this.renderProp.createDelegate(this);
62529 };
62530
62531 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
62532     
62533     
62534     nameText : 'Name',
62535     valueText : 'Value',
62536     
62537     dateFormat : 'm/j/Y',
62538     
62539     
62540     renderDate : function(dateVal){
62541         return dateVal.dateFormat(this.dateFormat);
62542     },
62543
62544     renderBool : function(bVal){
62545         return bVal ? 'true' : 'false';
62546     },
62547
62548     isCellEditable : function(colIndex, rowIndex){
62549         return colIndex == 1;
62550     },
62551
62552     getRenderer : function(col){
62553         return col == 1 ?
62554             this.renderCellDelegate : this.renderPropDelegate;
62555     },
62556
62557     renderProp : function(v){
62558         return this.getPropertyName(v);
62559     },
62560
62561     renderCell : function(val){
62562         var rv = val;
62563         if(val instanceof Date){
62564             rv = this.renderDate(val);
62565         }else if(typeof val == 'boolean'){
62566             rv = this.renderBool(val);
62567         }
62568         return Roo.util.Format.htmlEncode(rv);
62569     },
62570
62571     getPropertyName : function(name){
62572         var pn = this.grid.propertyNames;
62573         return pn && pn[name] ? pn[name] : name;
62574     },
62575
62576     getCellEditor : function(colIndex, rowIndex){
62577         var p = this.store.getProperty(rowIndex);
62578         var n = p.data['name'], val = p.data['value'];
62579         
62580         if(typeof(this.grid.customEditors[n]) == 'string'){
62581             return this.editors[this.grid.customEditors[n]];
62582         }
62583         if(typeof(this.grid.customEditors[n]) != 'undefined'){
62584             return this.grid.customEditors[n];
62585         }
62586         if(val instanceof Date){
62587             return this.editors['date'];
62588         }else if(typeof val == 'number'){
62589             return this.editors['number'];
62590         }else if(typeof val == 'boolean'){
62591             return this.editors['boolean'];
62592         }else{
62593             return this.editors['string'];
62594         }
62595     }
62596 });
62597
62598 /**
62599  * @class Roo.grid.PropertyGrid
62600  * @extends Roo.grid.EditorGrid
62601  * This class represents the  interface of a component based property grid control.
62602  * <br><br>Usage:<pre><code>
62603  var grid = new Roo.grid.PropertyGrid("my-container-id", {
62604       
62605  });
62606  // set any options
62607  grid.render();
62608  * </code></pre>
62609   
62610  * @constructor
62611  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62612  * The container MUST have some type of size defined for the grid to fill. The container will be
62613  * automatically set to position relative if it isn't already.
62614  * @param {Object} config A config object that sets properties on this grid.
62615  */
62616 Roo.grid.PropertyGrid = function(container, config){
62617     config = config || {};
62618     var store = new Roo.grid.PropertyStore(this);
62619     this.store = store;
62620     var cm = new Roo.grid.PropertyColumnModel(this, store);
62621     store.store.sort('name', 'ASC');
62622     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
62623         ds: store.store,
62624         cm: cm,
62625         enableColLock:false,
62626         enableColumnMove:false,
62627         stripeRows:false,
62628         trackMouseOver: false,
62629         clicksToEdit:1
62630     }, config));
62631     this.getGridEl().addClass('x-props-grid');
62632     this.lastEditRow = null;
62633     this.on('columnresize', this.onColumnResize, this);
62634     this.addEvents({
62635          /**
62636              * @event beforepropertychange
62637              * Fires before a property changes (return false to stop?)
62638              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62639              * @param {String} id Record Id
62640              * @param {String} newval New Value
62641          * @param {String} oldval Old Value
62642              */
62643         "beforepropertychange": true,
62644         /**
62645              * @event propertychange
62646              * Fires after a property changes
62647              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62648              * @param {String} id Record Id
62649              * @param {String} newval New Value
62650          * @param {String} oldval Old Value
62651              */
62652         "propertychange": true
62653     });
62654     this.customEditors = this.customEditors || {};
62655 };
62656 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
62657     
62658      /**
62659      * @cfg {Object} customEditors map of colnames=> custom editors.
62660      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
62661      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
62662      * false disables editing of the field.
62663          */
62664     
62665       /**
62666      * @cfg {Object} propertyNames map of property Names to their displayed value
62667          */
62668     
62669     render : function(){
62670         Roo.grid.PropertyGrid.superclass.render.call(this);
62671         this.autoSize.defer(100, this);
62672     },
62673
62674     autoSize : function(){
62675         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
62676         if(this.view){
62677             this.view.fitColumns();
62678         }
62679     },
62680
62681     onColumnResize : function(){
62682         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
62683         this.autoSize();
62684     },
62685     /**
62686      * Sets the data for the Grid
62687      * accepts a Key => Value object of all the elements avaiable.
62688      * @param {Object} data  to appear in grid.
62689      */
62690     setSource : function(source){
62691         this.store.setSource(source);
62692         //this.autoSize();
62693     },
62694     /**
62695      * Gets all the data from the grid.
62696      * @return {Object} data  data stored in grid
62697      */
62698     getSource : function(){
62699         return this.store.getSource();
62700     }
62701 });/*
62702   
62703  * Licence LGPL
62704  
62705  */
62706  
62707 /**
62708  * @class Roo.grid.Calendar
62709  * @extends Roo.grid.Grid
62710  * This class extends the Grid to provide a calendar widget
62711  * <br><br>Usage:<pre><code>
62712  var grid = new Roo.grid.Calendar("my-container-id", {
62713      ds: myDataStore,
62714      cm: myColModel,
62715      selModel: mySelectionModel,
62716      autoSizeColumns: true,
62717      monitorWindowResize: false,
62718      trackMouseOver: true
62719      eventstore : real data store..
62720  });
62721  // set any options
62722  grid.render();
62723   
62724   * @constructor
62725  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62726  * The container MUST have some type of size defined for the grid to fill. The container will be
62727  * automatically set to position relative if it isn't already.
62728  * @param {Object} config A config object that sets properties on this grid.
62729  */
62730 Roo.grid.Calendar = function(container, config){
62731         // initialize the container
62732         this.container = Roo.get(container);
62733         this.container.update("");
62734         this.container.setStyle("overflow", "hidden");
62735     this.container.addClass('x-grid-container');
62736
62737     this.id = this.container.id;
62738
62739     Roo.apply(this, config);
62740     // check and correct shorthanded configs
62741     
62742     var rows = [];
62743     var d =1;
62744     for (var r = 0;r < 6;r++) {
62745         
62746         rows[r]=[];
62747         for (var c =0;c < 7;c++) {
62748             rows[r][c]= '';
62749         }
62750     }
62751     if (this.eventStore) {
62752         this.eventStore= Roo.factory(this.eventStore, Roo.data);
62753         this.eventStore.on('load',this.onLoad, this);
62754         this.eventStore.on('beforeload',this.clearEvents, this);
62755          
62756     }
62757     
62758     this.dataSource = new Roo.data.Store({
62759             proxy: new Roo.data.MemoryProxy(rows),
62760             reader: new Roo.data.ArrayReader({}, [
62761                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
62762     });
62763
62764     this.dataSource.load();
62765     this.ds = this.dataSource;
62766     this.ds.xmodule = this.xmodule || false;
62767     
62768     
62769     var cellRender = function(v,x,r)
62770     {
62771         return String.format(
62772             '<div class="fc-day  fc-widget-content"><div>' +
62773                 '<div class="fc-event-container"></div>' +
62774                 '<div class="fc-day-number">{0}</div>'+
62775                 
62776                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
62777             '</div></div>', v);
62778     
62779     }
62780     
62781     
62782     this.colModel = new Roo.grid.ColumnModel( [
62783         {
62784             xtype: 'ColumnModel',
62785             xns: Roo.grid,
62786             dataIndex : 'weekday0',
62787             header : 'Sunday',
62788             renderer : cellRender
62789         },
62790         {
62791             xtype: 'ColumnModel',
62792             xns: Roo.grid,
62793             dataIndex : 'weekday1',
62794             header : 'Monday',
62795             renderer : cellRender
62796         },
62797         {
62798             xtype: 'ColumnModel',
62799             xns: Roo.grid,
62800             dataIndex : 'weekday2',
62801             header : 'Tuesday',
62802             renderer : cellRender
62803         },
62804         {
62805             xtype: 'ColumnModel',
62806             xns: Roo.grid,
62807             dataIndex : 'weekday3',
62808             header : 'Wednesday',
62809             renderer : cellRender
62810         },
62811         {
62812             xtype: 'ColumnModel',
62813             xns: Roo.grid,
62814             dataIndex : 'weekday4',
62815             header : 'Thursday',
62816             renderer : cellRender
62817         },
62818         {
62819             xtype: 'ColumnModel',
62820             xns: Roo.grid,
62821             dataIndex : 'weekday5',
62822             header : 'Friday',
62823             renderer : cellRender
62824         },
62825         {
62826             xtype: 'ColumnModel',
62827             xns: Roo.grid,
62828             dataIndex : 'weekday6',
62829             header : 'Saturday',
62830             renderer : cellRender
62831         }
62832     ]);
62833     this.cm = this.colModel;
62834     this.cm.xmodule = this.xmodule || false;
62835  
62836         
62837           
62838     //this.selModel = new Roo.grid.CellSelectionModel();
62839     //this.sm = this.selModel;
62840     //this.selModel.init(this);
62841     
62842     
62843     if(this.width){
62844         this.container.setWidth(this.width);
62845     }
62846
62847     if(this.height){
62848         this.container.setHeight(this.height);
62849     }
62850     /** @private */
62851         this.addEvents({
62852         // raw events
62853         /**
62854          * @event click
62855          * The raw click event for the entire grid.
62856          * @param {Roo.EventObject} e
62857          */
62858         "click" : true,
62859         /**
62860          * @event dblclick
62861          * The raw dblclick event for the entire grid.
62862          * @param {Roo.EventObject} e
62863          */
62864         "dblclick" : true,
62865         /**
62866          * @event contextmenu
62867          * The raw contextmenu event for the entire grid.
62868          * @param {Roo.EventObject} e
62869          */
62870         "contextmenu" : true,
62871         /**
62872          * @event mousedown
62873          * The raw mousedown event for the entire grid.
62874          * @param {Roo.EventObject} e
62875          */
62876         "mousedown" : true,
62877         /**
62878          * @event mouseup
62879          * The raw mouseup event for the entire grid.
62880          * @param {Roo.EventObject} e
62881          */
62882         "mouseup" : true,
62883         /**
62884          * @event mouseover
62885          * The raw mouseover event for the entire grid.
62886          * @param {Roo.EventObject} e
62887          */
62888         "mouseover" : true,
62889         /**
62890          * @event mouseout
62891          * The raw mouseout event for the entire grid.
62892          * @param {Roo.EventObject} e
62893          */
62894         "mouseout" : true,
62895         /**
62896          * @event keypress
62897          * The raw keypress event for the entire grid.
62898          * @param {Roo.EventObject} e
62899          */
62900         "keypress" : true,
62901         /**
62902          * @event keydown
62903          * The raw keydown event for the entire grid.
62904          * @param {Roo.EventObject} e
62905          */
62906         "keydown" : true,
62907
62908         // custom events
62909
62910         /**
62911          * @event cellclick
62912          * Fires when a cell is clicked
62913          * @param {Grid} this
62914          * @param {Number} rowIndex
62915          * @param {Number} columnIndex
62916          * @param {Roo.EventObject} e
62917          */
62918         "cellclick" : true,
62919         /**
62920          * @event celldblclick
62921          * Fires when a cell is double clicked
62922          * @param {Grid} this
62923          * @param {Number} rowIndex
62924          * @param {Number} columnIndex
62925          * @param {Roo.EventObject} e
62926          */
62927         "celldblclick" : true,
62928         /**
62929          * @event rowclick
62930          * Fires when a row is clicked
62931          * @param {Grid} this
62932          * @param {Number} rowIndex
62933          * @param {Roo.EventObject} e
62934          */
62935         "rowclick" : true,
62936         /**
62937          * @event rowdblclick
62938          * Fires when a row is double clicked
62939          * @param {Grid} this
62940          * @param {Number} rowIndex
62941          * @param {Roo.EventObject} e
62942          */
62943         "rowdblclick" : true,
62944         /**
62945          * @event headerclick
62946          * Fires when a header is clicked
62947          * @param {Grid} this
62948          * @param {Number} columnIndex
62949          * @param {Roo.EventObject} e
62950          */
62951         "headerclick" : true,
62952         /**
62953          * @event headerdblclick
62954          * Fires when a header cell is double clicked
62955          * @param {Grid} this
62956          * @param {Number} columnIndex
62957          * @param {Roo.EventObject} e
62958          */
62959         "headerdblclick" : true,
62960         /**
62961          * @event rowcontextmenu
62962          * Fires when a row is right clicked
62963          * @param {Grid} this
62964          * @param {Number} rowIndex
62965          * @param {Roo.EventObject} e
62966          */
62967         "rowcontextmenu" : true,
62968         /**
62969          * @event cellcontextmenu
62970          * Fires when a cell is right clicked
62971          * @param {Grid} this
62972          * @param {Number} rowIndex
62973          * @param {Number} cellIndex
62974          * @param {Roo.EventObject} e
62975          */
62976          "cellcontextmenu" : true,
62977         /**
62978          * @event headercontextmenu
62979          * Fires when a header is right clicked
62980          * @param {Grid} this
62981          * @param {Number} columnIndex
62982          * @param {Roo.EventObject} e
62983          */
62984         "headercontextmenu" : true,
62985         /**
62986          * @event bodyscroll
62987          * Fires when the body element is scrolled
62988          * @param {Number} scrollLeft
62989          * @param {Number} scrollTop
62990          */
62991         "bodyscroll" : true,
62992         /**
62993          * @event columnresize
62994          * Fires when the user resizes a column
62995          * @param {Number} columnIndex
62996          * @param {Number} newSize
62997          */
62998         "columnresize" : true,
62999         /**
63000          * @event columnmove
63001          * Fires when the user moves a column
63002          * @param {Number} oldIndex
63003          * @param {Number} newIndex
63004          */
63005         "columnmove" : true,
63006         /**
63007          * @event startdrag
63008          * Fires when row(s) start being dragged
63009          * @param {Grid} this
63010          * @param {Roo.GridDD} dd The drag drop object
63011          * @param {event} e The raw browser event
63012          */
63013         "startdrag" : true,
63014         /**
63015          * @event enddrag
63016          * Fires when a drag operation is complete
63017          * @param {Grid} this
63018          * @param {Roo.GridDD} dd The drag drop object
63019          * @param {event} e The raw browser event
63020          */
63021         "enddrag" : true,
63022         /**
63023          * @event dragdrop
63024          * Fires when dragged row(s) are dropped on a valid DD target
63025          * @param {Grid} this
63026          * @param {Roo.GridDD} dd The drag drop object
63027          * @param {String} targetId The target drag drop object
63028          * @param {event} e The raw browser event
63029          */
63030         "dragdrop" : true,
63031         /**
63032          * @event dragover
63033          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
63034          * @param {Grid} this
63035          * @param {Roo.GridDD} dd The drag drop object
63036          * @param {String} targetId The target drag drop object
63037          * @param {event} e The raw browser event
63038          */
63039         "dragover" : true,
63040         /**
63041          * @event dragenter
63042          *  Fires when the dragged row(s) first cross another DD target while being dragged
63043          * @param {Grid} this
63044          * @param {Roo.GridDD} dd The drag drop object
63045          * @param {String} targetId The target drag drop object
63046          * @param {event} e The raw browser event
63047          */
63048         "dragenter" : true,
63049         /**
63050          * @event dragout
63051          * Fires when the dragged row(s) leave another DD target while being dragged
63052          * @param {Grid} this
63053          * @param {Roo.GridDD} dd The drag drop object
63054          * @param {String} targetId The target drag drop object
63055          * @param {event} e The raw browser event
63056          */
63057         "dragout" : true,
63058         /**
63059          * @event rowclass
63060          * Fires when a row is rendered, so you can change add a style to it.
63061          * @param {GridView} gridview   The grid view
63062          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
63063          */
63064         'rowclass' : true,
63065
63066         /**
63067          * @event render
63068          * Fires when the grid is rendered
63069          * @param {Grid} grid
63070          */
63071         'render' : true,
63072             /**
63073              * @event select
63074              * Fires when a date is selected
63075              * @param {DatePicker} this
63076              * @param {Date} date The selected date
63077              */
63078         'select': true,
63079         /**
63080              * @event monthchange
63081              * Fires when the displayed month changes 
63082              * @param {DatePicker} this
63083              * @param {Date} date The selected month
63084              */
63085         'monthchange': true,
63086         /**
63087              * @event evententer
63088              * Fires when mouse over an event
63089              * @param {Calendar} this
63090              * @param {event} Event
63091              */
63092         'evententer': true,
63093         /**
63094              * @event eventleave
63095              * Fires when the mouse leaves an
63096              * @param {Calendar} this
63097              * @param {event}
63098              */
63099         'eventleave': true,
63100         /**
63101              * @event eventclick
63102              * Fires when the mouse click an
63103              * @param {Calendar} this
63104              * @param {event}
63105              */
63106         'eventclick': true,
63107         /**
63108              * @event eventrender
63109              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
63110              * @param {Calendar} this
63111              * @param {data} data to be modified
63112              */
63113         'eventrender': true
63114         
63115     });
63116
63117     Roo.grid.Grid.superclass.constructor.call(this);
63118     this.on('render', function() {
63119         this.view.el.addClass('x-grid-cal'); 
63120         
63121         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
63122
63123     },this);
63124     
63125     if (!Roo.grid.Calendar.style) {
63126         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
63127             
63128             
63129             '.x-grid-cal .x-grid-col' :  {
63130                 height: 'auto !important',
63131                 'vertical-align': 'top'
63132             },
63133             '.x-grid-cal  .fc-event-hori' : {
63134                 height: '14px'
63135             }
63136              
63137             
63138         }, Roo.id());
63139     }
63140
63141     
63142     
63143 };
63144 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
63145     /**
63146      * @cfg {Store} eventStore The store that loads events.
63147      */
63148     eventStore : 25,
63149
63150      
63151     activeDate : false,
63152     startDay : 0,
63153     autoWidth : true,
63154     monitorWindowResize : false,
63155
63156     
63157     resizeColumns : function() {
63158         var col = (this.view.el.getWidth() / 7) - 3;
63159         // loop through cols, and setWidth
63160         for(var i =0 ; i < 7 ; i++){
63161             this.cm.setColumnWidth(i, col);
63162         }
63163     },
63164      setDate :function(date) {
63165         
63166         Roo.log('setDate?');
63167         
63168         this.resizeColumns();
63169         var vd = this.activeDate;
63170         this.activeDate = date;
63171 //        if(vd && this.el){
63172 //            var t = date.getTime();
63173 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
63174 //                Roo.log('using add remove');
63175 //                
63176 //                this.fireEvent('monthchange', this, date);
63177 //                
63178 //                this.cells.removeClass("fc-state-highlight");
63179 //                this.cells.each(function(c){
63180 //                   if(c.dateValue == t){
63181 //                       c.addClass("fc-state-highlight");
63182 //                       setTimeout(function(){
63183 //                            try{c.dom.firstChild.focus();}catch(e){}
63184 //                       }, 50);
63185 //                       return false;
63186 //                   }
63187 //                   return true;
63188 //                });
63189 //                return;
63190 //            }
63191 //        }
63192         
63193         var days = date.getDaysInMonth();
63194         
63195         var firstOfMonth = date.getFirstDateOfMonth();
63196         var startingPos = firstOfMonth.getDay()-this.startDay;
63197         
63198         if(startingPos < this.startDay){
63199             startingPos += 7;
63200         }
63201         
63202         var pm = date.add(Date.MONTH, -1);
63203         var prevStart = pm.getDaysInMonth()-startingPos;
63204 //        
63205         
63206         
63207         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63208         
63209         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
63210         //this.cells.addClassOnOver('fc-state-hover');
63211         
63212         var cells = this.cells.elements;
63213         var textEls = this.textNodes;
63214         
63215         //Roo.each(cells, function(cell){
63216         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
63217         //});
63218         
63219         days += startingPos;
63220
63221         // convert everything to numbers so it's fast
63222         var day = 86400000;
63223         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
63224         //Roo.log(d);
63225         //Roo.log(pm);
63226         //Roo.log(prevStart);
63227         
63228         var today = new Date().clearTime().getTime();
63229         var sel = date.clearTime().getTime();
63230         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
63231         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
63232         var ddMatch = this.disabledDatesRE;
63233         var ddText = this.disabledDatesText;
63234         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
63235         var ddaysText = this.disabledDaysText;
63236         var format = this.format;
63237         
63238         var setCellClass = function(cal, cell){
63239             
63240             //Roo.log('set Cell Class');
63241             cell.title = "";
63242             var t = d.getTime();
63243             
63244             //Roo.log(d);
63245             
63246             
63247             cell.dateValue = t;
63248             if(t == today){
63249                 cell.className += " fc-today";
63250                 cell.className += " fc-state-highlight";
63251                 cell.title = cal.todayText;
63252             }
63253             if(t == sel){
63254                 // disable highlight in other month..
63255                 cell.className += " fc-state-highlight";
63256                 
63257             }
63258             // disabling
63259             if(t < min) {
63260                 //cell.className = " fc-state-disabled";
63261                 cell.title = cal.minText;
63262                 return;
63263             }
63264             if(t > max) {
63265                 //cell.className = " fc-state-disabled";
63266                 cell.title = cal.maxText;
63267                 return;
63268             }
63269             if(ddays){
63270                 if(ddays.indexOf(d.getDay()) != -1){
63271                     // cell.title = ddaysText;
63272                    // cell.className = " fc-state-disabled";
63273                 }
63274             }
63275             if(ddMatch && format){
63276                 var fvalue = d.dateFormat(format);
63277                 if(ddMatch.test(fvalue)){
63278                     cell.title = ddText.replace("%0", fvalue);
63279                    cell.className = " fc-state-disabled";
63280                 }
63281             }
63282             
63283             if (!cell.initialClassName) {
63284                 cell.initialClassName = cell.dom.className;
63285             }
63286             
63287             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
63288         };
63289
63290         var i = 0;
63291         
63292         for(; i < startingPos; i++) {
63293             cells[i].dayName =  (++prevStart);
63294             Roo.log(textEls[i]);
63295             d.setDate(d.getDate()+1);
63296             
63297             //cells[i].className = "fc-past fc-other-month";
63298             setCellClass(this, cells[i]);
63299         }
63300         
63301         var intDay = 0;
63302         
63303         for(; i < days; i++){
63304             intDay = i - startingPos + 1;
63305             cells[i].dayName =  (intDay);
63306             d.setDate(d.getDate()+1);
63307             
63308             cells[i].className = ''; // "x-date-active";
63309             setCellClass(this, cells[i]);
63310         }
63311         var extraDays = 0;
63312         
63313         for(; i < 42; i++) {
63314             //textEls[i].innerHTML = (++extraDays);
63315             
63316             d.setDate(d.getDate()+1);
63317             cells[i].dayName = (++extraDays);
63318             cells[i].className = "fc-future fc-other-month";
63319             setCellClass(this, cells[i]);
63320         }
63321         
63322         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
63323         
63324         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
63325         
63326         // this will cause all the cells to mis
63327         var rows= [];
63328         var i =0;
63329         for (var r = 0;r < 6;r++) {
63330             for (var c =0;c < 7;c++) {
63331                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
63332             }    
63333         }
63334         
63335         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63336         for(i=0;i<cells.length;i++) {
63337             
63338             this.cells.elements[i].dayName = cells[i].dayName ;
63339             this.cells.elements[i].className = cells[i].className;
63340             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
63341             this.cells.elements[i].title = cells[i].title ;
63342             this.cells.elements[i].dateValue = cells[i].dateValue ;
63343         }
63344         
63345         
63346         
63347         
63348         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
63349         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
63350         
63351         ////if(totalRows != 6){
63352             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
63353            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
63354        // }
63355         
63356         this.fireEvent('monthchange', this, date);
63357         
63358         
63359     },
63360  /**
63361      * Returns the grid's SelectionModel.
63362      * @return {SelectionModel}
63363      */
63364     getSelectionModel : function(){
63365         if(!this.selModel){
63366             this.selModel = new Roo.grid.CellSelectionModel();
63367         }
63368         return this.selModel;
63369     },
63370
63371     load: function() {
63372         this.eventStore.load()
63373         
63374         
63375         
63376     },
63377     
63378     findCell : function(dt) {
63379         dt = dt.clearTime().getTime();
63380         var ret = false;
63381         this.cells.each(function(c){
63382             //Roo.log("check " +c.dateValue + '?=' + dt);
63383             if(c.dateValue == dt){
63384                 ret = c;
63385                 return false;
63386             }
63387             return true;
63388         });
63389         
63390         return ret;
63391     },
63392     
63393     findCells : function(rec) {
63394         var s = rec.data.start_dt.clone().clearTime().getTime();
63395        // Roo.log(s);
63396         var e= rec.data.end_dt.clone().clearTime().getTime();
63397        // Roo.log(e);
63398         var ret = [];
63399         this.cells.each(function(c){
63400              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
63401             
63402             if(c.dateValue > e){
63403                 return ;
63404             }
63405             if(c.dateValue < s){
63406                 return ;
63407             }
63408             ret.push(c);
63409         });
63410         
63411         return ret;    
63412     },
63413     
63414     findBestRow: function(cells)
63415     {
63416         var ret = 0;
63417         
63418         for (var i =0 ; i < cells.length;i++) {
63419             ret  = Math.max(cells[i].rows || 0,ret);
63420         }
63421         return ret;
63422         
63423     },
63424     
63425     
63426     addItem : function(rec)
63427     {
63428         // look for vertical location slot in
63429         var cells = this.findCells(rec);
63430         
63431         rec.row = this.findBestRow(cells);
63432         
63433         // work out the location.
63434         
63435         var crow = false;
63436         var rows = [];
63437         for(var i =0; i < cells.length; i++) {
63438             if (!crow) {
63439                 crow = {
63440                     start : cells[i],
63441                     end :  cells[i]
63442                 };
63443                 continue;
63444             }
63445             if (crow.start.getY() == cells[i].getY()) {
63446                 // on same row.
63447                 crow.end = cells[i];
63448                 continue;
63449             }
63450             // different row.
63451             rows.push(crow);
63452             crow = {
63453                 start: cells[i],
63454                 end : cells[i]
63455             };
63456             
63457         }
63458         
63459         rows.push(crow);
63460         rec.els = [];
63461         rec.rows = rows;
63462         rec.cells = cells;
63463         for (var i = 0; i < cells.length;i++) {
63464             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
63465             
63466         }
63467         
63468         
63469     },
63470     
63471     clearEvents: function() {
63472         
63473         if (!this.eventStore.getCount()) {
63474             return;
63475         }
63476         // reset number of rows in cells.
63477         Roo.each(this.cells.elements, function(c){
63478             c.rows = 0;
63479         });
63480         
63481         this.eventStore.each(function(e) {
63482             this.clearEvent(e);
63483         },this);
63484         
63485     },
63486     
63487     clearEvent : function(ev)
63488     {
63489         if (ev.els) {
63490             Roo.each(ev.els, function(el) {
63491                 el.un('mouseenter' ,this.onEventEnter, this);
63492                 el.un('mouseleave' ,this.onEventLeave, this);
63493                 el.remove();
63494             },this);
63495             ev.els = [];
63496         }
63497     },
63498     
63499     
63500     renderEvent : function(ev,ctr) {
63501         if (!ctr) {
63502              ctr = this.view.el.select('.fc-event-container',true).first();
63503         }
63504         
63505          
63506         this.clearEvent(ev);
63507             //code
63508        
63509         
63510         
63511         ev.els = [];
63512         var cells = ev.cells;
63513         var rows = ev.rows;
63514         this.fireEvent('eventrender', this, ev);
63515         
63516         for(var i =0; i < rows.length; i++) {
63517             
63518             cls = '';
63519             if (i == 0) {
63520                 cls += ' fc-event-start';
63521             }
63522             if ((i+1) == rows.length) {
63523                 cls += ' fc-event-end';
63524             }
63525             
63526             //Roo.log(ev.data);
63527             // how many rows should it span..
63528             var cg = this.eventTmpl.append(ctr,Roo.apply({
63529                 fccls : cls
63530                 
63531             }, ev.data) , true);
63532             
63533             
63534             cg.on('mouseenter' ,this.onEventEnter, this, ev);
63535             cg.on('mouseleave' ,this.onEventLeave, this, ev);
63536             cg.on('click', this.onEventClick, this, ev);
63537             
63538             ev.els.push(cg);
63539             
63540             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
63541             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
63542             //Roo.log(cg);
63543              
63544             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
63545             cg.setWidth(ebox.right - sbox.x -2);
63546         }
63547     },
63548     
63549     renderEvents: function()
63550     {   
63551         // first make sure there is enough space..
63552         
63553         if (!this.eventTmpl) {
63554             this.eventTmpl = new Roo.Template(
63555                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
63556                     '<div class="fc-event-inner">' +
63557                         '<span class="fc-event-time">{time}</span>' +
63558                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
63559                     '</div>' +
63560                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
63561                 '</div>'
63562             );
63563                 
63564         }
63565                
63566         
63567         
63568         this.cells.each(function(c) {
63569             //Roo.log(c.select('.fc-day-content div',true).first());
63570             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
63571         });
63572         
63573         var ctr = this.view.el.select('.fc-event-container',true).first();
63574         
63575         var cls;
63576         this.eventStore.each(function(ev){
63577             
63578             this.renderEvent(ev);
63579              
63580              
63581         }, this);
63582         this.view.layout();
63583         
63584     },
63585     
63586     onEventEnter: function (e, el,event,d) {
63587         this.fireEvent('evententer', this, el, event);
63588     },
63589     
63590     onEventLeave: function (e, el,event,d) {
63591         this.fireEvent('eventleave', this, el, event);
63592     },
63593     
63594     onEventClick: function (e, el,event,d) {
63595         this.fireEvent('eventclick', this, el, event);
63596     },
63597     
63598     onMonthChange: function () {
63599         this.store.load();
63600     },
63601     
63602     onLoad: function () {
63603         
63604         //Roo.log('calendar onload');
63605 //         
63606         if(this.eventStore.getCount() > 0){
63607             
63608            
63609             
63610             this.eventStore.each(function(d){
63611                 
63612                 
63613                 // FIXME..
63614                 var add =   d.data;
63615                 if (typeof(add.end_dt) == 'undefined')  {
63616                     Roo.log("Missing End time in calendar data: ");
63617                     Roo.log(d);
63618                     return;
63619                 }
63620                 if (typeof(add.start_dt) == 'undefined')  {
63621                     Roo.log("Missing Start time in calendar data: ");
63622                     Roo.log(d);
63623                     return;
63624                 }
63625                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
63626                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
63627                 add.id = add.id || d.id;
63628                 add.title = add.title || '??';
63629                 
63630                 this.addItem(d);
63631                 
63632              
63633             },this);
63634         }
63635         
63636         this.renderEvents();
63637     }
63638     
63639
63640 });
63641 /*
63642  grid : {
63643                 xtype: 'Grid',
63644                 xns: Roo.grid,
63645                 listeners : {
63646                     render : function ()
63647                     {
63648                         _this.grid = this;
63649                         
63650                         if (!this.view.el.hasClass('course-timesheet')) {
63651                             this.view.el.addClass('course-timesheet');
63652                         }
63653                         if (this.tsStyle) {
63654                             this.ds.load({});
63655                             return; 
63656                         }
63657                         Roo.log('width');
63658                         Roo.log(_this.grid.view.el.getWidth());
63659                         
63660                         
63661                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
63662                             '.course-timesheet .x-grid-row' : {
63663                                 height: '80px'
63664                             },
63665                             '.x-grid-row td' : {
63666                                 'vertical-align' : 0
63667                             },
63668                             '.course-edit-link' : {
63669                                 'color' : 'blue',
63670                                 'text-overflow' : 'ellipsis',
63671                                 'overflow' : 'hidden',
63672                                 'white-space' : 'nowrap',
63673                                 'cursor' : 'pointer'
63674                             },
63675                             '.sub-link' : {
63676                                 'color' : 'green'
63677                             },
63678                             '.de-act-sup-link' : {
63679                                 'color' : 'purple',
63680                                 'text-decoration' : 'line-through'
63681                             },
63682                             '.de-act-link' : {
63683                                 'color' : 'red',
63684                                 'text-decoration' : 'line-through'
63685                             },
63686                             '.course-timesheet .course-highlight' : {
63687                                 'border-top-style': 'dashed !important',
63688                                 'border-bottom-bottom': 'dashed !important'
63689                             },
63690                             '.course-timesheet .course-item' : {
63691                                 'font-family'   : 'tahoma, arial, helvetica',
63692                                 'font-size'     : '11px',
63693                                 'overflow'      : 'hidden',
63694                                 'padding-left'  : '10px',
63695                                 'padding-right' : '10px',
63696                                 'padding-top' : '10px' 
63697                             }
63698                             
63699                         }, Roo.id());
63700                                 this.ds.load({});
63701                     }
63702                 },
63703                 autoWidth : true,
63704                 monitorWindowResize : false,
63705                 cellrenderer : function(v,x,r)
63706                 {
63707                     return v;
63708                 },
63709                 sm : {
63710                     xtype: 'CellSelectionModel',
63711                     xns: Roo.grid
63712                 },
63713                 dataSource : {
63714                     xtype: 'Store',
63715                     xns: Roo.data,
63716                     listeners : {
63717                         beforeload : function (_self, options)
63718                         {
63719                             options.params = options.params || {};
63720                             options.params._month = _this.monthField.getValue();
63721                             options.params.limit = 9999;
63722                             options.params['sort'] = 'when_dt';    
63723                             options.params['dir'] = 'ASC';    
63724                             this.proxy.loadResponse = this.loadResponse;
63725                             Roo.log("load?");
63726                             //this.addColumns();
63727                         },
63728                         load : function (_self, records, options)
63729                         {
63730                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
63731                                 // if you click on the translation.. you can edit it...
63732                                 var el = Roo.get(this);
63733                                 var id = el.dom.getAttribute('data-id');
63734                                 var d = el.dom.getAttribute('data-date');
63735                                 var t = el.dom.getAttribute('data-time');
63736                                 //var id = this.child('span').dom.textContent;
63737                                 
63738                                 //Roo.log(this);
63739                                 Pman.Dialog.CourseCalendar.show({
63740                                     id : id,
63741                                     when_d : d,
63742                                     when_t : t,
63743                                     productitem_active : id ? 1 : 0
63744                                 }, function() {
63745                                     _this.grid.ds.load({});
63746                                 });
63747                            
63748                            });
63749                            
63750                            _this.panel.fireEvent('resize', [ '', '' ]);
63751                         }
63752                     },
63753                     loadResponse : function(o, success, response){
63754                             // this is overridden on before load..
63755                             
63756                             Roo.log("our code?");       
63757                             //Roo.log(success);
63758                             //Roo.log(response)
63759                             delete this.activeRequest;
63760                             if(!success){
63761                                 this.fireEvent("loadexception", this, o, response);
63762                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
63763                                 return;
63764                             }
63765                             var result;
63766                             try {
63767                                 result = o.reader.read(response);
63768                             }catch(e){
63769                                 Roo.log("load exception?");
63770                                 this.fireEvent("loadexception", this, o, response, e);
63771                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
63772                                 return;
63773                             }
63774                             Roo.log("ready...");        
63775                             // loop through result.records;
63776                             // and set this.tdate[date] = [] << array of records..
63777                             _this.tdata  = {};
63778                             Roo.each(result.records, function(r){
63779                                 //Roo.log(r.data);
63780                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
63781                                     _this.tdata[r.data.when_dt.format('j')] = [];
63782                                 }
63783                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
63784                             });
63785                             
63786                             //Roo.log(_this.tdata);
63787                             
63788                             result.records = [];
63789                             result.totalRecords = 6;
63790                     
63791                             // let's generate some duumy records for the rows.
63792                             //var st = _this.dateField.getValue();
63793                             
63794                             // work out monday..
63795                             //st = st.add(Date.DAY, -1 * st.format('w'));
63796                             
63797                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63798                             
63799                             var firstOfMonth = date.getFirstDayOfMonth();
63800                             var days = date.getDaysInMonth();
63801                             var d = 1;
63802                             var firstAdded = false;
63803                             for (var i = 0; i < result.totalRecords ; i++) {
63804                                 //var d= st.add(Date.DAY, i);
63805                                 var row = {};
63806                                 var added = 0;
63807                                 for(var w = 0 ; w < 7 ; w++){
63808                                     if(!firstAdded && firstOfMonth != w){
63809                                         continue;
63810                                     }
63811                                     if(d > days){
63812                                         continue;
63813                                     }
63814                                     firstAdded = true;
63815                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
63816                                     row['weekday'+w] = String.format(
63817                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
63818                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
63819                                                     d,
63820                                                     date.format('Y-m-')+dd
63821                                                 );
63822                                     added++;
63823                                     if(typeof(_this.tdata[d]) != 'undefined'){
63824                                         Roo.each(_this.tdata[d], function(r){
63825                                             var is_sub = '';
63826                                             var deactive = '';
63827                                             var id = r.id;
63828                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
63829                                             if(r.parent_id*1>0){
63830                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
63831                                                 id = r.parent_id;
63832                                             }
63833                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
63834                                                 deactive = 'de-act-link';
63835                                             }
63836                                             
63837                                             row['weekday'+w] += String.format(
63838                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
63839                                                     id, //0
63840                                                     r.product_id_name, //1
63841                                                     r.when_dt.format('h:ia'), //2
63842                                                     is_sub, //3
63843                                                     deactive, //4
63844                                                     desc // 5
63845                                             );
63846                                         });
63847                                     }
63848                                     d++;
63849                                 }
63850                                 
63851                                 // only do this if something added..
63852                                 if(added > 0){ 
63853                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
63854                                 }
63855                                 
63856                                 
63857                                 // push it twice. (second one with an hour..
63858                                 
63859                             }
63860                             //Roo.log(result);
63861                             this.fireEvent("load", this, o, o.request.arg);
63862                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
63863                         },
63864                     sortInfo : {field: 'when_dt', direction : 'ASC' },
63865                     proxy : {
63866                         xtype: 'HttpProxy',
63867                         xns: Roo.data,
63868                         method : 'GET',
63869                         url : baseURL + '/Roo/Shop_course.php'
63870                     },
63871                     reader : {
63872                         xtype: 'JsonReader',
63873                         xns: Roo.data,
63874                         id : 'id',
63875                         fields : [
63876                             {
63877                                 'name': 'id',
63878                                 'type': 'int'
63879                             },
63880                             {
63881                                 'name': 'when_dt',
63882                                 'type': 'string'
63883                             },
63884                             {
63885                                 'name': 'end_dt',
63886                                 'type': 'string'
63887                             },
63888                             {
63889                                 'name': 'parent_id',
63890                                 'type': 'int'
63891                             },
63892                             {
63893                                 'name': 'product_id',
63894                                 'type': 'int'
63895                             },
63896                             {
63897                                 'name': 'productitem_id',
63898                                 'type': 'int'
63899                             },
63900                             {
63901                                 'name': 'guid',
63902                                 'type': 'int'
63903                             }
63904                         ]
63905                     }
63906                 },
63907                 toolbar : {
63908                     xtype: 'Toolbar',
63909                     xns: Roo,
63910                     items : [
63911                         {
63912                             xtype: 'Button',
63913                             xns: Roo.Toolbar,
63914                             listeners : {
63915                                 click : function (_self, e)
63916                                 {
63917                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63918                                     sd.setMonth(sd.getMonth()-1);
63919                                     _this.monthField.setValue(sd.format('Y-m-d'));
63920                                     _this.grid.ds.load({});
63921                                 }
63922                             },
63923                             text : "Back"
63924                         },
63925                         {
63926                             xtype: 'Separator',
63927                             xns: Roo.Toolbar
63928                         },
63929                         {
63930                             xtype: 'MonthField',
63931                             xns: Roo.form,
63932                             listeners : {
63933                                 render : function (_self)
63934                                 {
63935                                     _this.monthField = _self;
63936                                    // _this.monthField.set  today
63937                                 },
63938                                 select : function (combo, date)
63939                                 {
63940                                     _this.grid.ds.load({});
63941                                 }
63942                             },
63943                             value : (function() { return new Date(); })()
63944                         },
63945                         {
63946                             xtype: 'Separator',
63947                             xns: Roo.Toolbar
63948                         },
63949                         {
63950                             xtype: 'TextItem',
63951                             xns: Roo.Toolbar,
63952                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
63953                         },
63954                         {
63955                             xtype: 'Fill',
63956                             xns: Roo.Toolbar
63957                         },
63958                         {
63959                             xtype: 'Button',
63960                             xns: Roo.Toolbar,
63961                             listeners : {
63962                                 click : function (_self, e)
63963                                 {
63964                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63965                                     sd.setMonth(sd.getMonth()+1);
63966                                     _this.monthField.setValue(sd.format('Y-m-d'));
63967                                     _this.grid.ds.load({});
63968                                 }
63969                             },
63970                             text : "Next"
63971                         }
63972                     ]
63973                 },
63974                  
63975             }
63976         };
63977         
63978         *//*
63979  * Based on:
63980  * Ext JS Library 1.1.1
63981  * Copyright(c) 2006-2007, Ext JS, LLC.
63982  *
63983  * Originally Released Under LGPL - original licence link has changed is not relivant.
63984  *
63985  * Fork - LGPL
63986  * <script type="text/javascript">
63987  */
63988  
63989 /**
63990  * @class Roo.LoadMask
63991  * A simple utility class for generically masking elements while loading data.  If the element being masked has
63992  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
63993  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
63994  * element's UpdateManager load indicator and will be destroyed after the initial load.
63995  * @constructor
63996  * Create a new LoadMask
63997  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
63998  * @param {Object} config The config object
63999  */
64000 Roo.LoadMask = function(el, config){
64001     this.el = Roo.get(el);
64002     Roo.apply(this, config);
64003     if(this.store){
64004         this.store.on('beforeload', this.onBeforeLoad, this);
64005         this.store.on('load', this.onLoad, this);
64006         this.store.on('loadexception', this.onLoadException, this);
64007         this.removeMask = false;
64008     }else{
64009         var um = this.el.getUpdateManager();
64010         um.showLoadIndicator = false; // disable the default indicator
64011         um.on('beforeupdate', this.onBeforeLoad, this);
64012         um.on('update', this.onLoad, this);
64013         um.on('failure', this.onLoad, this);
64014         this.removeMask = true;
64015     }
64016 };
64017
64018 Roo.LoadMask.prototype = {
64019     /**
64020      * @cfg {Boolean} removeMask
64021      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
64022      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
64023      */
64024     removeMask : false,
64025     /**
64026      * @cfg {String} msg
64027      * The text to display in a centered loading message box (defaults to 'Loading...')
64028      */
64029     msg : 'Loading...',
64030     /**
64031      * @cfg {String} msgCls
64032      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
64033      */
64034     msgCls : 'x-mask-loading',
64035
64036     /**
64037      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
64038      * @type Boolean
64039      */
64040     disabled: false,
64041
64042     /**
64043      * Disables the mask to prevent it from being displayed
64044      */
64045     disable : function(){
64046        this.disabled = true;
64047     },
64048
64049     /**
64050      * Enables the mask so that it can be displayed
64051      */
64052     enable : function(){
64053         this.disabled = false;
64054     },
64055     
64056     onLoadException : function()
64057     {
64058         Roo.log(arguments);
64059         
64060         if (typeof(arguments[3]) != 'undefined') {
64061             Roo.MessageBox.alert("Error loading",arguments[3]);
64062         } 
64063         /*
64064         try {
64065             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
64066                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
64067             }   
64068         } catch(e) {
64069             
64070         }
64071         */
64072     
64073         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64074     },
64075     // private
64076     onLoad : function()
64077     {
64078         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64079     },
64080
64081     // private
64082     onBeforeLoad : function(){
64083         if(!this.disabled){
64084             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
64085         }
64086     },
64087
64088     // private
64089     destroy : function(){
64090         if(this.store){
64091             this.store.un('beforeload', this.onBeforeLoad, this);
64092             this.store.un('load', this.onLoad, this);
64093             this.store.un('loadexception', this.onLoadException, this);
64094         }else{
64095             var um = this.el.getUpdateManager();
64096             um.un('beforeupdate', this.onBeforeLoad, this);
64097             um.un('update', this.onLoad, this);
64098             um.un('failure', this.onLoad, this);
64099         }
64100     }
64101 };/*
64102  * Based on:
64103  * Ext JS Library 1.1.1
64104  * Copyright(c) 2006-2007, Ext JS, LLC.
64105  *
64106  * Originally Released Under LGPL - original licence link has changed is not relivant.
64107  *
64108  * Fork - LGPL
64109  * <script type="text/javascript">
64110  */
64111
64112
64113 /**
64114  * @class Roo.XTemplate
64115  * @extends Roo.Template
64116  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
64117 <pre><code>
64118 var t = new Roo.XTemplate(
64119         '&lt;select name="{name}"&gt;',
64120                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
64121         '&lt;/select&gt;'
64122 );
64123  
64124 // then append, applying the master template values
64125  </code></pre>
64126  *
64127  * Supported features:
64128  *
64129  *  Tags:
64130
64131 <pre><code>
64132       {a_variable} - output encoded.
64133       {a_variable.format:("Y-m-d")} - call a method on the variable
64134       {a_variable:raw} - unencoded output
64135       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
64136       {a_variable:this.method_on_template(...)} - call a method on the template object.
64137  
64138 </code></pre>
64139  *  The tpl tag:
64140 <pre><code>
64141         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
64142         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
64143         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
64144         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
64145   
64146         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
64147         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
64148 </code></pre>
64149  *      
64150  */
64151 Roo.XTemplate = function()
64152 {
64153     Roo.XTemplate.superclass.constructor.apply(this, arguments);
64154     if (this.html) {
64155         this.compile();
64156     }
64157 };
64158
64159
64160 Roo.extend(Roo.XTemplate, Roo.Template, {
64161
64162     /**
64163      * The various sub templates
64164      */
64165     tpls : false,
64166     /**
64167      *
64168      * basic tag replacing syntax
64169      * WORD:WORD()
64170      *
64171      * // you can fake an object call by doing this
64172      *  x.t:(test,tesT) 
64173      * 
64174      */
64175     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
64176
64177     /**
64178      * compile the template
64179      *
64180      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
64181      *
64182      */
64183     compile: function()
64184     {
64185         var s = this.html;
64186      
64187         s = ['<tpl>', s, '</tpl>'].join('');
64188     
64189         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
64190             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
64191             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
64192             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
64193             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
64194             m,
64195             id     = 0,
64196             tpls   = [];
64197     
64198         while(true == !!(m = s.match(re))){
64199             var forMatch   = m[0].match(nameRe),
64200                 ifMatch   = m[0].match(ifRe),
64201                 execMatch   = m[0].match(execRe),
64202                 namedMatch   = m[0].match(namedRe),
64203                 
64204                 exp  = null, 
64205                 fn   = null,
64206                 exec = null,
64207                 name = forMatch && forMatch[1] ? forMatch[1] : '';
64208                 
64209             if (ifMatch) {
64210                 // if - puts fn into test..
64211                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
64212                 if(exp){
64213                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
64214                 }
64215             }
64216             
64217             if (execMatch) {
64218                 // exec - calls a function... returns empty if true is  returned.
64219                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
64220                 if(exp){
64221                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
64222                 }
64223             }
64224             
64225             
64226             if (name) {
64227                 // for = 
64228                 switch(name){
64229                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
64230                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
64231                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
64232                 }
64233             }
64234             var uid = namedMatch ? namedMatch[1] : id;
64235             
64236             
64237             tpls.push({
64238                 id:     namedMatch ? namedMatch[1] : id,
64239                 target: name,
64240                 exec:   exec,
64241                 test:   fn,
64242                 body:   m[1] || ''
64243             });
64244             if (namedMatch) {
64245                 s = s.replace(m[0], '');
64246             } else { 
64247                 s = s.replace(m[0], '{xtpl'+ id + '}');
64248             }
64249             ++id;
64250         }
64251         this.tpls = [];
64252         for(var i = tpls.length-1; i >= 0; --i){
64253             this.compileTpl(tpls[i]);
64254             this.tpls[tpls[i].id] = tpls[i];
64255         }
64256         this.master = tpls[tpls.length-1];
64257         return this;
64258     },
64259     /**
64260      * same as applyTemplate, except it's done to one of the subTemplates
64261      * when using named templates, you can do:
64262      *
64263      * var str = pl.applySubTemplate('your-name', values);
64264      *
64265      * 
64266      * @param {Number} id of the template
64267      * @param {Object} values to apply to template
64268      * @param {Object} parent (normaly the instance of this object)
64269      */
64270     applySubTemplate : function(id, values, parent)
64271     {
64272         
64273         
64274         var t = this.tpls[id];
64275         
64276         
64277         try { 
64278             if(t.test && !t.test.call(this, values, parent)){
64279                 return '';
64280             }
64281         } catch(e) {
64282             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
64283             Roo.log(e.toString());
64284             Roo.log(t.test);
64285             return ''
64286         }
64287         try { 
64288             
64289             if(t.exec && t.exec.call(this, values, parent)){
64290                 return '';
64291             }
64292         } catch(e) {
64293             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
64294             Roo.log(e.toString());
64295             Roo.log(t.exec);
64296             return ''
64297         }
64298         try {
64299             var vs = t.target ? t.target.call(this, values, parent) : values;
64300             parent = t.target ? values : parent;
64301             if(t.target && vs instanceof Array){
64302                 var buf = [];
64303                 for(var i = 0, len = vs.length; i < len; i++){
64304                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
64305                 }
64306                 return buf.join('');
64307             }
64308             return t.compiled.call(this, vs, parent);
64309         } catch (e) {
64310             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
64311             Roo.log(e.toString());
64312             Roo.log(t.compiled);
64313             return '';
64314         }
64315     },
64316
64317     compileTpl : function(tpl)
64318     {
64319         var fm = Roo.util.Format;
64320         var useF = this.disableFormats !== true;
64321         var sep = Roo.isGecko ? "+" : ",";
64322         var undef = function(str) {
64323             Roo.log("Property not found :"  + str);
64324             return '';
64325         };
64326         
64327         var fn = function(m, name, format, args)
64328         {
64329             //Roo.log(arguments);
64330             args = args ? args.replace(/\\'/g,"'") : args;
64331             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
64332             if (typeof(format) == 'undefined') {
64333                 format= 'htmlEncode';
64334             }
64335             if (format == 'raw' ) {
64336                 format = false;
64337             }
64338             
64339             if(name.substr(0, 4) == 'xtpl'){
64340                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
64341             }
64342             
64343             // build an array of options to determine if value is undefined..
64344             
64345             // basically get 'xxxx.yyyy' then do
64346             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
64347             //    (function () { Roo.log("Property not found"); return ''; })() :
64348             //    ......
64349             
64350             var udef_ar = [];
64351             var lookfor = '';
64352             Roo.each(name.split('.'), function(st) {
64353                 lookfor += (lookfor.length ? '.': '') + st;
64354                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
64355             });
64356             
64357             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
64358             
64359             
64360             if(format && useF){
64361                 
64362                 args = args ? ',' + args : "";
64363                  
64364                 if(format.substr(0, 5) != "this."){
64365                     format = "fm." + format + '(';
64366                 }else{
64367                     format = 'this.call("'+ format.substr(5) + '", ';
64368                     args = ", values";
64369                 }
64370                 
64371                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
64372             }
64373              
64374             if (args.length) {
64375                 // called with xxyx.yuu:(test,test)
64376                 // change to ()
64377                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
64378             }
64379             // raw.. - :raw modifier..
64380             return "'"+ sep + udef_st  + name + ")"+sep+"'";
64381             
64382         };
64383         var body;
64384         // branched to use + in gecko and [].join() in others
64385         if(Roo.isGecko){
64386             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
64387                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
64388                     "';};};";
64389         }else{
64390             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
64391             body.push(tpl.body.replace(/(\r\n|\n)/g,
64392                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
64393             body.push("'].join('');};};");
64394             body = body.join('');
64395         }
64396         
64397         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
64398        
64399         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
64400         eval(body);
64401         
64402         return this;
64403     },
64404
64405     applyTemplate : function(values){
64406         return this.master.compiled.call(this, values, {});
64407         //var s = this.subs;
64408     },
64409
64410     apply : function(){
64411         return this.applyTemplate.apply(this, arguments);
64412     }
64413
64414  });
64415
64416 Roo.XTemplate.from = function(el){
64417     el = Roo.getDom(el);
64418     return new Roo.XTemplate(el.value || el.innerHTML);
64419 };