roojs-bootstrap.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 /**
959   * Make the first letter of a string uppercase
960   *
961   * @return {String} The new string.
962   */
963 String.prototype.toUpperCaseFirst = function () {
964     return this.charAt(0).toUpperCase() + this.slice(1);
965 };  
966   
967 /*
968  * Based on:
969  * Ext JS Library 1.1.1
970  * Copyright(c) 2006-2007, Ext JS, LLC.
971  *
972  * Originally Released Under LGPL - original licence link has changed is not relivant.
973  *
974  * Fork - LGPL
975  * <script type="text/javascript">
976  */
977
978  /**
979  * @class Number
980  */
981 Roo.applyIf(Number.prototype, {
982     /**
983      * Checks whether or not the current number is within a desired range.  If the number is already within the
984      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
985      * exceeded.  Note that this method returns the constrained value but does not change the current number.
986      * @param {Number} min The minimum number in the range
987      * @param {Number} max The maximum number in the range
988      * @return {Number} The constrained value if outside the range, otherwise the current value
989      */
990     constrain : function(min, max){
991         return Math.min(Math.max(this, min), max);
992     }
993 });/*
994  * Based on:
995  * Ext JS Library 1.1.1
996  * Copyright(c) 2006-2007, Ext JS, LLC.
997  *
998  * Originally Released Under LGPL - original licence link has changed is not relivant.
999  *
1000  * Fork - LGPL
1001  * <script type="text/javascript">
1002  */
1003  /**
1004  * @class Array
1005  */
1006 Roo.applyIf(Array.prototype, {
1007     /**
1008      * 
1009      * Checks whether or not the specified object exists in the array.
1010      * @param {Object} o The object to check for
1011      * @return {Number} The index of o in the array (or -1 if it is not found)
1012      */
1013     indexOf : function(o){
1014        for (var i = 0, len = this.length; i < len; i++){
1015               if(this[i] == o) { return i; }
1016        }
1017            return -1;
1018     },
1019
1020     /**
1021      * Removes the specified object from the array.  If the object is not found nothing happens.
1022      * @param {Object} o The object to remove
1023      */
1024     remove : function(o){
1025        var index = this.indexOf(o);
1026        if(index != -1){
1027            this.splice(index, 1);
1028        }
1029     },
1030     /**
1031      * Map (JS 1.6 compatibility)
1032      * @param {Function} function  to call
1033      */
1034     map : function(fun )
1035     {
1036         var len = this.length >>> 0;
1037         if (typeof fun != "function") {
1038             throw new TypeError();
1039         }
1040         var res = new Array(len);
1041         var thisp = arguments[1];
1042         for (var i = 0; i < len; i++)
1043         {
1044             if (i in this) {
1045                 res[i] = fun.call(thisp, this[i], i, this);
1046             }
1047         }
1048
1049         return res;
1050     },
1051     /**
1052      * equals
1053      * @param {Array} o The array to compare to
1054      * @returns {Boolean} true if the same
1055      */
1056     equals : function(b)
1057     {
1058             // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1059         if (this === b) {
1060             return true;
1061         }
1062         if (b == null) {
1063             return false;
1064         }
1065         if (this.length !== b.length) {
1066             return false;
1067         }
1068           
1069         // sort?? a.sort().equals(b.sort());
1070           
1071         for (var i = 0; i < this.length; ++i) {
1072             if (this[i] !== b[i]) {
1073             return false;
1074             }
1075         }
1076         return true;
1077     } 
1078     
1079     
1080     
1081     
1082 });
1083
1084 Roo.applyIf(Array, {
1085  /**
1086      * from
1087      * @static
1088      * @param {Array} o Or Array like object (eg. nodelist)
1089      * @returns {Array} 
1090      */
1091     from : function(o)
1092     {
1093         var ret= [];
1094     
1095         for (var i =0; i < o.length; i++) { 
1096             ret[i] = o[i];
1097         }
1098         return ret;
1099       
1100     }
1101 });
1102 /*
1103  * Based on:
1104  * Ext JS Library 1.1.1
1105  * Copyright(c) 2006-2007, Ext JS, LLC.
1106  *
1107  * Originally Released Under LGPL - original licence link has changed is not relivant.
1108  *
1109  * Fork - LGPL
1110  * <script type="text/javascript">
1111  */
1112
1113 /**
1114  * @class Date
1115  *
1116  * The date parsing and format syntax is a subset of
1117  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1118  * supported will provide results equivalent to their PHP versions.
1119  *
1120  * Following is the list of all currently supported formats:
1121  *<pre>
1122 Sample date:
1123 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1124
1125 Format  Output      Description
1126 ------  ----------  --------------------------------------------------------------
1127   d      10         Day of the month, 2 digits with leading zeros
1128   D      Wed        A textual representation of a day, three letters
1129   j      10         Day of the month without leading zeros
1130   l      Wednesday  A full textual representation of the day of the week
1131   S      th         English ordinal day of month suffix, 2 chars (use with j)
1132   w      3          Numeric representation of the day of the week
1133   z      9          The julian date, or day of the year (0-365)
1134   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1135   F      January    A full textual representation of the month
1136   m      01         Numeric representation of a month, with leading zeros
1137   M      Jan        Month name abbreviation, three letters
1138   n      1          Numeric representation of a month, without leading zeros
1139   t      31         Number of days in the given month
1140   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1141   Y      2007       A full numeric representation of a year, 4 digits
1142   y      07         A two digit representation of a year
1143   a      pm         Lowercase Ante meridiem and Post meridiem
1144   A      PM         Uppercase Ante meridiem and Post meridiem
1145   g      3          12-hour format of an hour without leading zeros
1146   G      15         24-hour format of an hour without leading zeros
1147   h      03         12-hour format of an hour with leading zeros
1148   H      15         24-hour format of an hour with leading zeros
1149   i      05         Minutes with leading zeros
1150   s      01         Seconds, with leading zeros
1151   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1152   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1153   T      CST        Timezone setting of the machine running the code
1154   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1155 </pre>
1156  *
1157  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1158  * <pre><code>
1159 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1160 document.write(dt.format('Y-m-d'));                         //2007-01-10
1161 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1162 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
1163  </code></pre>
1164  *
1165  * Here are some standard date/time patterns that you might find helpful.  They
1166  * are not part of the source of Date.js, but to use them you can simply copy this
1167  * block of code into any script that is included after Date.js and they will also become
1168  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1169  * <pre><code>
1170 Date.patterns = {
1171     ISO8601Long:"Y-m-d H:i:s",
1172     ISO8601Short:"Y-m-d",
1173     ShortDate: "n/j/Y",
1174     LongDate: "l, F d, Y",
1175     FullDateTime: "l, F d, Y g:i:s A",
1176     MonthDay: "F d",
1177     ShortTime: "g:i A",
1178     LongTime: "g:i:s A",
1179     SortableDateTime: "Y-m-d\\TH:i:s",
1180     UniversalSortableDateTime: "Y-m-d H:i:sO",
1181     YearMonth: "F, Y"
1182 };
1183 </code></pre>
1184  *
1185  * Example usage:
1186  * <pre><code>
1187 var dt = new Date();
1188 document.write(dt.format(Date.patterns.ShortDate));
1189  </code></pre>
1190  */
1191
1192 /*
1193  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1194  * They generate precompiled functions from date formats instead of parsing and
1195  * processing the pattern every time you format a date.  These functions are available
1196  * on every Date object (any javascript function).
1197  *
1198  * The original article and download are here:
1199  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1200  *
1201  */
1202  
1203  
1204  // was in core
1205 /**
1206  Returns the number of milliseconds between this date and date
1207  @param {Date} date (optional) Defaults to now
1208  @return {Number} The diff in milliseconds
1209  @member Date getElapsed
1210  */
1211 Date.prototype.getElapsed = function(date) {
1212         return Math.abs((date || new Date()).getTime()-this.getTime());
1213 };
1214 // was in date file..
1215
1216
1217 // private
1218 Date.parseFunctions = {count:0};
1219 // private
1220 Date.parseRegexes = [];
1221 // private
1222 Date.formatFunctions = {count:0};
1223
1224 // private
1225 Date.prototype.dateFormat = function(format) {
1226     if (Date.formatFunctions[format] == null) {
1227         Date.createNewFormat(format);
1228     }
1229     var func = Date.formatFunctions[format];
1230     return this[func]();
1231 };
1232
1233
1234 /**
1235  * Formats a date given the supplied format string
1236  * @param {String} format The format string
1237  * @return {String} The formatted date
1238  * @method
1239  */
1240 Date.prototype.format = Date.prototype.dateFormat;
1241
1242 // private
1243 Date.createNewFormat = function(format) {
1244     var funcName = "format" + Date.formatFunctions.count++;
1245     Date.formatFunctions[format] = funcName;
1246     var code = "Date.prototype." + funcName + " = function(){return ";
1247     var special = false;
1248     var ch = '';
1249     for (var i = 0; i < format.length; ++i) {
1250         ch = format.charAt(i);
1251         if (!special && ch == "\\") {
1252             special = true;
1253         }
1254         else if (special) {
1255             special = false;
1256             code += "'" + String.escape(ch) + "' + ";
1257         }
1258         else {
1259             code += Date.getFormatCode(ch);
1260         }
1261     }
1262     /** eval:var:zzzzzzzzzzzzz */
1263     eval(code.substring(0, code.length - 3) + ";}");
1264 };
1265
1266 // private
1267 Date.getFormatCode = function(character) {
1268     switch (character) {
1269     case "d":
1270         return "String.leftPad(this.getDate(), 2, '0') + ";
1271     case "D":
1272         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1273     case "j":
1274         return "this.getDate() + ";
1275     case "l":
1276         return "Date.dayNames[this.getDay()] + ";
1277     case "S":
1278         return "this.getSuffix() + ";
1279     case "w":
1280         return "this.getDay() + ";
1281     case "z":
1282         return "this.getDayOfYear() + ";
1283     case "W":
1284         return "this.getWeekOfYear() + ";
1285     case "F":
1286         return "Date.monthNames[this.getMonth()] + ";
1287     case "m":
1288         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1289     case "M":
1290         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1291     case "n":
1292         return "(this.getMonth() + 1) + ";
1293     case "t":
1294         return "this.getDaysInMonth() + ";
1295     case "L":
1296         return "(this.isLeapYear() ? 1 : 0) + ";
1297     case "Y":
1298         return "this.getFullYear() + ";
1299     case "y":
1300         return "('' + this.getFullYear()).substring(2, 4) + ";
1301     case "a":
1302         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1303     case "A":
1304         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1305     case "g":
1306         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1307     case "G":
1308         return "this.getHours() + ";
1309     case "h":
1310         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1311     case "H":
1312         return "String.leftPad(this.getHours(), 2, '0') + ";
1313     case "i":
1314         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1315     case "s":
1316         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1317     case "O":
1318         return "this.getGMTOffset() + ";
1319     case "P":
1320         return "this.getGMTColonOffset() + ";
1321     case "T":
1322         return "this.getTimezone() + ";
1323     case "Z":
1324         return "(this.getTimezoneOffset() * -60) + ";
1325     default:
1326         return "'" + String.escape(character) + "' + ";
1327     }
1328 };
1329
1330 /**
1331  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1332  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1333  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1334  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1335  * string or the parse operation will fail.
1336  * Example Usage:
1337 <pre><code>
1338 //dt = Fri May 25 2007 (current date)
1339 var dt = new Date();
1340
1341 //dt = Thu May 25 2006 (today's month/day in 2006)
1342 dt = Date.parseDate("2006", "Y");
1343
1344 //dt = Sun Jan 15 2006 (all date parts specified)
1345 dt = Date.parseDate("2006-1-15", "Y-m-d");
1346
1347 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1348 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1349 </code></pre>
1350  * @param {String} input The unparsed date as a string
1351  * @param {String} format The format the date is in
1352  * @return {Date} The parsed date
1353  * @static
1354  */
1355 Date.parseDate = function(input, format) {
1356     if (Date.parseFunctions[format] == null) {
1357         Date.createParser(format);
1358     }
1359     var func = Date.parseFunctions[format];
1360     return Date[func](input);
1361 };
1362 /**
1363  * @private
1364  */
1365
1366 Date.createParser = function(format) {
1367     var funcName = "parse" + Date.parseFunctions.count++;
1368     var regexNum = Date.parseRegexes.length;
1369     var currentGroup = 1;
1370     Date.parseFunctions[format] = funcName;
1371
1372     var code = "Date." + funcName + " = function(input){\n"
1373         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1374         + "var d = new Date();\n"
1375         + "y = d.getFullYear();\n"
1376         + "m = d.getMonth();\n"
1377         + "d = d.getDate();\n"
1378         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1379         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1380         + "if (results && results.length > 0) {";
1381     var regex = "";
1382
1383     var special = false;
1384     var ch = '';
1385     for (var i = 0; i < format.length; ++i) {
1386         ch = format.charAt(i);
1387         if (!special && ch == "\\") {
1388             special = true;
1389         }
1390         else if (special) {
1391             special = false;
1392             regex += String.escape(ch);
1393         }
1394         else {
1395             var obj = Date.formatCodeToRegex(ch, currentGroup);
1396             currentGroup += obj.g;
1397             regex += obj.s;
1398             if (obj.g && obj.c) {
1399                 code += obj.c;
1400             }
1401         }
1402     }
1403
1404     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1405         + "{v = new Date(y, m, d, h, i, s); v.setFullYear(y);}\n"
1406         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1407         + "{v = new Date(y, m, d, h, i); v.setFullYear(y);}\n"
1408         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1409         + "{v = new Date(y, m, d, h); v.setFullYear(y);}\n"
1410         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1411         + "{v = new Date(y, m, d); v.setFullYear(y);}\n"
1412         + "else if (y >= 0 && m >= 0)\n"
1413         + "{v = new Date(y, m); v.setFullYear(y);}\n"
1414         + "else if (y >= 0)\n"
1415         + "{v = new Date(y); v.setFullYear(y);}\n"
1416         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1417         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1418         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1419         + ";}";
1420
1421     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1422     /** eval:var:zzzzzzzzzzzzz */
1423     eval(code);
1424 };
1425
1426 // private
1427 Date.formatCodeToRegex = function(character, currentGroup) {
1428     switch (character) {
1429     case "D":
1430         return {g:0,
1431         c:null,
1432         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1433     case "j":
1434         return {g:1,
1435             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1436             s:"(\\d{1,2})"}; // day of month without leading zeroes
1437     case "d":
1438         return {g:1,
1439             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1440             s:"(\\d{2})"}; // day of month with leading zeroes
1441     case "l":
1442         return {g:0,
1443             c:null,
1444             s:"(?:" + Date.dayNames.join("|") + ")"};
1445     case "S":
1446         return {g:0,
1447             c:null,
1448             s:"(?:st|nd|rd|th)"};
1449     case "w":
1450         return {g:0,
1451             c:null,
1452             s:"\\d"};
1453     case "z":
1454         return {g:0,
1455             c:null,
1456             s:"(?:\\d{1,3})"};
1457     case "W":
1458         return {g:0,
1459             c:null,
1460             s:"(?:\\d{2})"};
1461     case "F":
1462         return {g:1,
1463             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1464             s:"(" + Date.monthNames.join("|") + ")"};
1465     case "M":
1466         return {g:1,
1467             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1468             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1469     case "n":
1470         return {g:1,
1471             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1472             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1473     case "m":
1474         return {g:1,
1475             c:"m = Math.max(0,parseInt(results[" + currentGroup + "], 10) - 1);\n",
1476             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1477     case "t":
1478         return {g:0,
1479             c:null,
1480             s:"\\d{1,2}"};
1481     case "L":
1482         return {g:0,
1483             c:null,
1484             s:"(?:1|0)"};
1485     case "Y":
1486         return {g:1,
1487             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1488             s:"(\\d{4})"};
1489     case "y":
1490         return {g:1,
1491             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1492                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1493             s:"(\\d{1,2})"};
1494     case "a":
1495         return {g:1,
1496             c:"if (results[" + currentGroup + "] == 'am') {\n"
1497                 + "if (h == 12) { h = 0; }\n"
1498                 + "} else { if (h < 12) { h += 12; }}",
1499             s:"(am|pm)"};
1500     case "A":
1501         return {g:1,
1502             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1503                 + "if (h == 12) { h = 0; }\n"
1504                 + "} else { if (h < 12) { h += 12; }}",
1505             s:"(AM|PM)"};
1506     case "g":
1507     case "G":
1508         return {g:1,
1509             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1510             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1511     case "h":
1512     case "H":
1513         return {g:1,
1514             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1515             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1516     case "i":
1517         return {g:1,
1518             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1519             s:"(\\d{2})"};
1520     case "s":
1521         return {g:1,
1522             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1523             s:"(\\d{2})"};
1524     case "O":
1525         return {g:1,
1526             c:[
1527                 "o = results[", currentGroup, "];\n",
1528                 "var sn = o.substring(0,1);\n", // get + / - sign
1529                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1530                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1531                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1532                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1533             ].join(""),
1534             s:"([+\-]\\d{2,4})"};
1535     
1536     
1537     case "P":
1538         return {g:1,
1539                 c:[
1540                    "o = results[", currentGroup, "];\n",
1541                    "var sn = o.substring(0,1);\n",
1542                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1543                    "var mn = o.substring(4,6) % 60;\n",
1544                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1545                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1546             ].join(""),
1547             s:"([+\-]\\d{4})"};
1548     case "T":
1549         return {g:0,
1550             c:null,
1551             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1552     case "Z":
1553         return {g:1,
1554             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1555                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1556             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1557     default:
1558         return {g:0,
1559             c:null,
1560             s:String.escape(character)};
1561     }
1562 };
1563
1564 /**
1565  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1566  * @return {String} The abbreviated timezone name (e.g. 'CST')
1567  */
1568 Date.prototype.getTimezone = function() {
1569     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1570 };
1571
1572 /**
1573  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1574  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1575  */
1576 Date.prototype.getGMTOffset = function() {
1577     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1578         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1579         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1580 };
1581
1582 /**
1583  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1584  * @return {String} 2-characters representing hours and 2-characters representing minutes
1585  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1586  */
1587 Date.prototype.getGMTColonOffset = function() {
1588         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1589                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1590                 + ":"
1591                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1592 }
1593
1594 /**
1595  * Get the numeric day number of the year, adjusted for leap year.
1596  * @return {Number} 0 through 364 (365 in leap years)
1597  */
1598 Date.prototype.getDayOfYear = function() {
1599     var num = 0;
1600     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1601     for (var i = 0; i < this.getMonth(); ++i) {
1602         num += Date.daysInMonth[i];
1603     }
1604     return num + this.getDate() - 1;
1605 };
1606
1607 /**
1608  * Get the string representation of the numeric week number of the year
1609  * (equivalent to the format specifier 'W').
1610  * @return {String} '00' through '52'
1611  */
1612 Date.prototype.getWeekOfYear = function() {
1613     // Skip to Thursday of this week
1614     var now = this.getDayOfYear() + (4 - this.getDay());
1615     // Find the first Thursday of the year
1616     var jan1 = new Date(this.getFullYear(), 0, 1);
1617     var then = (7 - jan1.getDay() + 4);
1618     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1619 };
1620
1621 /**
1622  * Whether or not the current date is in a leap year.
1623  * @return {Boolean} True if the current date is in a leap year, else false
1624  */
1625 Date.prototype.isLeapYear = function() {
1626     var year = this.getFullYear();
1627     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1628 };
1629
1630 /**
1631  * Get the first day of the current month, adjusted for leap year.  The returned value
1632  * is the numeric day index within the week (0-6) which can be used in conjunction with
1633  * the {@link #monthNames} array to retrieve the textual day name.
1634  * Example:
1635  *<pre><code>
1636 var dt = new Date('1/10/2007');
1637 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1638 </code></pre>
1639  * @return {Number} The day number (0-6)
1640  */
1641 Date.prototype.getFirstDayOfMonth = function() {
1642     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1643     return (day < 0) ? (day + 7) : day;
1644 };
1645
1646 /**
1647  * Get the last day of the current month, adjusted for leap year.  The returned value
1648  * is the numeric day index within the week (0-6) which can be used in conjunction with
1649  * the {@link #monthNames} array to retrieve the textual day name.
1650  * Example:
1651  *<pre><code>
1652 var dt = new Date('1/10/2007');
1653 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1654 </code></pre>
1655  * @return {Number} The day number (0-6)
1656  */
1657 Date.prototype.getLastDayOfMonth = function() {
1658     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1659     return (day < 0) ? (day + 7) : day;
1660 };
1661
1662
1663 /**
1664  * Get the first date of this date's month
1665  * @return {Date}
1666  */
1667 Date.prototype.getFirstDateOfMonth = function() {
1668     return new Date(this.getFullYear(), this.getMonth(), 1);
1669 };
1670
1671 /**
1672  * Get the last date of this date's month
1673  * @return {Date}
1674  */
1675 Date.prototype.getLastDateOfMonth = function() {
1676     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1677 };
1678 /**
1679  * Get the number of days in the current month, adjusted for leap year.
1680  * @return {Number} The number of days in the month
1681  */
1682 Date.prototype.getDaysInMonth = function() {
1683     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1684     return Date.daysInMonth[this.getMonth()];
1685 };
1686
1687 /**
1688  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1689  * @return {String} 'st, 'nd', 'rd' or 'th'
1690  */
1691 Date.prototype.getSuffix = function() {
1692     switch (this.getDate()) {
1693         case 1:
1694         case 21:
1695         case 31:
1696             return "st";
1697         case 2:
1698         case 22:
1699             return "nd";
1700         case 3:
1701         case 23:
1702             return "rd";
1703         default:
1704             return "th";
1705     }
1706 };
1707
1708 // private
1709 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1710
1711 /**
1712  * An array of textual month names.
1713  * Override these values for international dates, for example...
1714  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1715  * @type Array
1716  * @static
1717  */
1718 Date.monthNames =
1719    ["January",
1720     "February",
1721     "March",
1722     "April",
1723     "May",
1724     "June",
1725     "July",
1726     "August",
1727     "September",
1728     "October",
1729     "November",
1730     "December"];
1731
1732 /**
1733  * An array of textual day names.
1734  * Override these values for international dates, for example...
1735  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1736  * @type Array
1737  * @static
1738  */
1739 Date.dayNames =
1740    ["Sunday",
1741     "Monday",
1742     "Tuesday",
1743     "Wednesday",
1744     "Thursday",
1745     "Friday",
1746     "Saturday"];
1747
1748 // private
1749 Date.y2kYear = 50;
1750 // private
1751 Date.monthNumbers = {
1752     Jan:0,
1753     Feb:1,
1754     Mar:2,
1755     Apr:3,
1756     May:4,
1757     Jun:5,
1758     Jul:6,
1759     Aug:7,
1760     Sep:8,
1761     Oct:9,
1762     Nov:10,
1763     Dec:11};
1764
1765 /**
1766  * Creates and returns a new Date instance with the exact same date value as the called instance.
1767  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1768  * variable will also be changed.  When the intention is to create a new variable that will not
1769  * modify the original instance, you should create a clone.
1770  *
1771  * Example of correctly cloning a date:
1772  * <pre><code>
1773 //wrong way:
1774 var orig = new Date('10/1/2006');
1775 var copy = orig;
1776 copy.setDate(5);
1777 document.write(orig);  //returns 'Thu Oct 05 2006'!
1778
1779 //correct way:
1780 var orig = new Date('10/1/2006');
1781 var copy = orig.clone();
1782 copy.setDate(5);
1783 document.write(orig);  //returns 'Thu Oct 01 2006'
1784 </code></pre>
1785  * @return {Date} The new Date instance
1786  */
1787 Date.prototype.clone = function() {
1788         return new Date(this.getTime());
1789 };
1790
1791 /**
1792  * Clears any time information from this date
1793  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1794  @return {Date} this or the clone
1795  */
1796 Date.prototype.clearTime = function(clone){
1797     if(clone){
1798         return this.clone().clearTime();
1799     }
1800     this.setHours(0);
1801     this.setMinutes(0);
1802     this.setSeconds(0);
1803     this.setMilliseconds(0);
1804     return this;
1805 };
1806
1807 // private
1808 // safari setMonth is broken -- check that this is only donw once...
1809 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1810     Date.brokenSetMonth = Date.prototype.setMonth;
1811         Date.prototype.setMonth = function(num){
1812                 if(num <= -1){
1813                         var n = Math.ceil(-num);
1814                         var back_year = Math.ceil(n/12);
1815                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1816                         this.setFullYear(this.getFullYear() - back_year);
1817                         return Date.brokenSetMonth.call(this, month);
1818                 } else {
1819                         return Date.brokenSetMonth.apply(this, arguments);
1820                 }
1821         };
1822 }
1823
1824 /** Date interval constant 
1825 * @static 
1826 * @type String */
1827 Date.MILLI = "ms";
1828 /** Date interval constant 
1829 * @static 
1830 * @type String */
1831 Date.SECOND = "s";
1832 /** Date interval constant 
1833 * @static 
1834 * @type String */
1835 Date.MINUTE = "mi";
1836 /** Date interval constant 
1837 * @static 
1838 * @type String */
1839 Date.HOUR = "h";
1840 /** Date interval constant 
1841 * @static 
1842 * @type String */
1843 Date.DAY = "d";
1844 /** Date interval constant 
1845 * @static 
1846 * @type String */
1847 Date.MONTH = "mo";
1848 /** Date interval constant 
1849 * @static 
1850 * @type String */
1851 Date.YEAR = "y";
1852
1853 /**
1854  * Provides a convenient method of performing basic date arithmetic.  This method
1855  * does not modify the Date instance being called - it creates and returns
1856  * a new Date instance containing the resulting date value.
1857  *
1858  * Examples:
1859  * <pre><code>
1860 //Basic usage:
1861 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1862 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1863
1864 //Negative values will subtract correctly:
1865 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1866 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1867
1868 //You can even chain several calls together in one line!
1869 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1870 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1871  </code></pre>
1872  *
1873  * @param {String} interval   A valid date interval enum value
1874  * @param {Number} value      The amount to add to the current date
1875  * @return {Date} The new Date instance
1876  */
1877 Date.prototype.add = function(interval, value){
1878   var d = this.clone();
1879   if (!interval || value === 0) { return d; }
1880   switch(interval.toLowerCase()){
1881     case Date.MILLI:
1882       d.setMilliseconds(this.getMilliseconds() + value);
1883       break;
1884     case Date.SECOND:
1885       d.setSeconds(this.getSeconds() + value);
1886       break;
1887     case Date.MINUTE:
1888       d.setMinutes(this.getMinutes() + value);
1889       break;
1890     case Date.HOUR:
1891       d.setHours(this.getHours() + value);
1892       break;
1893     case Date.DAY:
1894       d.setDate(this.getDate() + value);
1895       break;
1896     case Date.MONTH:
1897       var day = this.getDate();
1898       if(day > 28){
1899           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1900       }
1901       d.setDate(day);
1902       d.setMonth(this.getMonth() + value);
1903       break;
1904     case Date.YEAR:
1905       d.setFullYear(this.getFullYear() + value);
1906       break;
1907   }
1908   return d;
1909 };
1910 /**
1911  * @class Roo.lib.Dom
1912  * @licence LGPL
1913  * @static
1914  * 
1915  * Dom utils (from YIU afaik)
1916  *
1917  * 
1918  **/
1919 Roo.lib.Dom = {
1920     /**
1921      * Get the view width
1922      * @param {Boolean} full True will get the full document, otherwise it's the view width
1923      * @return {Number} The width
1924      */
1925      
1926     getViewWidth : function(full) {
1927         return full ? this.getDocumentWidth() : this.getViewportWidth();
1928     },
1929     /**
1930      * Get the view height
1931      * @param {Boolean} full True will get the full document, otherwise it's the view height
1932      * @return {Number} The height
1933      */
1934     getViewHeight : function(full) {
1935         return full ? this.getDocumentHeight() : this.getViewportHeight();
1936     },
1937     /**
1938      * Get the Full Document height 
1939      * @return {Number} The height
1940      */
1941     getDocumentHeight: function() {
1942         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1943         return Math.max(scrollHeight, this.getViewportHeight());
1944     },
1945     /**
1946      * Get the Full Document width
1947      * @return {Number} The width
1948      */
1949     getDocumentWidth: function() {
1950         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1951         return Math.max(scrollWidth, this.getViewportWidth());
1952     },
1953     /**
1954      * Get the Window Viewport height
1955      * @return {Number} The height
1956      */
1957     getViewportHeight: function() {
1958         var height = self.innerHeight;
1959         var mode = document.compatMode;
1960
1961         if ((mode || Roo.isIE) && !Roo.isOpera) {
1962             height = (mode == "CSS1Compat") ?
1963                      document.documentElement.clientHeight :
1964                      document.body.clientHeight;
1965         }
1966
1967         return height;
1968     },
1969     /**
1970      * Get the Window Viewport width
1971      * @return {Number} The width
1972      */
1973     getViewportWidth: function() {
1974         var width = self.innerWidth;
1975         var mode = document.compatMode;
1976
1977         if (mode || Roo.isIE) {
1978             width = (mode == "CSS1Compat") ?
1979                     document.documentElement.clientWidth :
1980                     document.body.clientWidth;
1981         }
1982         return width;
1983     },
1984
1985     isAncestor : function(p, c) {
1986         p = Roo.getDom(p);
1987         c = Roo.getDom(c);
1988         if (!p || !c) {
1989             return false;
1990         }
1991
1992         if (p.contains && !Roo.isSafari) {
1993             return p.contains(c);
1994         } else if (p.compareDocumentPosition) {
1995             return !!(p.compareDocumentPosition(c) & 16);
1996         } else {
1997             var parent = c.parentNode;
1998             while (parent) {
1999                 if (parent == p) {
2000                     return true;
2001                 }
2002                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
2003                     return false;
2004                 }
2005                 parent = parent.parentNode;
2006             }
2007             return false;
2008         }
2009     },
2010
2011     getRegion : function(el) {
2012         return Roo.lib.Region.getRegion(el);
2013     },
2014
2015     getY : function(el) {
2016         return this.getXY(el)[1];
2017     },
2018
2019     getX : function(el) {
2020         return this.getXY(el)[0];
2021     },
2022
2023     getXY : function(el) {
2024         var p, pe, b, scroll, bd = document.body;
2025         el = Roo.getDom(el);
2026         var fly = Roo.lib.AnimBase.fly;
2027         if (el.getBoundingClientRect) {
2028             b = el.getBoundingClientRect();
2029             scroll = fly(document).getScroll();
2030             return [b.left + scroll.left, b.top + scroll.top];
2031         }
2032         var x = 0, y = 0;
2033
2034         p = el;
2035
2036         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2037
2038         while (p) {
2039
2040             x += p.offsetLeft;
2041             y += p.offsetTop;
2042
2043             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2044                 hasAbsolute = true;
2045             }
2046
2047             if (Roo.isGecko) {
2048                 pe = fly(p);
2049
2050                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2051                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2052
2053
2054                 x += bl;
2055                 y += bt;
2056
2057
2058                 if (p != el && pe.getStyle('overflow') != 'visible') {
2059                     x += bl;
2060                     y += bt;
2061                 }
2062             }
2063             p = p.offsetParent;
2064         }
2065
2066         if (Roo.isSafari && hasAbsolute) {
2067             x -= bd.offsetLeft;
2068             y -= bd.offsetTop;
2069         }
2070
2071         if (Roo.isGecko && !hasAbsolute) {
2072             var dbd = fly(bd);
2073             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2074             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2075         }
2076
2077         p = el.parentNode;
2078         while (p && p != bd) {
2079             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2080                 x -= p.scrollLeft;
2081                 y -= p.scrollTop;
2082             }
2083             p = p.parentNode;
2084         }
2085         return [x, y];
2086     },
2087  
2088   
2089
2090
2091     setXY : function(el, xy) {
2092         el = Roo.fly(el, '_setXY');
2093         el.position();
2094         var pts = el.translatePoints(xy);
2095         if (xy[0] !== false) {
2096             el.dom.style.left = pts.left + "px";
2097         }
2098         if (xy[1] !== false) {
2099             el.dom.style.top = pts.top + "px";
2100         }
2101     },
2102
2103     setX : function(el, x) {
2104         this.setXY(el, [x, false]);
2105     },
2106
2107     setY : function(el, y) {
2108         this.setXY(el, [false, y]);
2109     }
2110 };
2111 /*
2112  * Portions of this file are based on pieces of Yahoo User Interface Library
2113  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2114  * YUI licensed under the BSD License:
2115  * http://developer.yahoo.net/yui/license.txt
2116  * <script type="text/javascript">
2117  *
2118  */
2119
2120 Roo.lib.Event = function() {
2121     var loadComplete = false;
2122     var listeners = [];
2123     var unloadListeners = [];
2124     var retryCount = 0;
2125     var onAvailStack = [];
2126     var counter = 0;
2127     var lastError = null;
2128
2129     return {
2130         POLL_RETRYS: 200,
2131         POLL_INTERVAL: 20,
2132         EL: 0,
2133         TYPE: 1,
2134         FN: 2,
2135         WFN: 3,
2136         OBJ: 3,
2137         ADJ_SCOPE: 4,
2138         _interval: null,
2139
2140         startInterval: function() {
2141             if (!this._interval) {
2142                 var self = this;
2143                 var callback = function() {
2144                     self._tryPreloadAttach();
2145                 };
2146                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2147
2148             }
2149         },
2150
2151         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2152             onAvailStack.push({ id:         p_id,
2153                 fn:         p_fn,
2154                 obj:        p_obj,
2155                 override:   p_override,
2156                 checkReady: false    });
2157
2158             retryCount = this.POLL_RETRYS;
2159             this.startInterval();
2160         },
2161
2162
2163         addListener: function(el, eventName, fn) {
2164             el = Roo.getDom(el);
2165             if (!el || !fn) {
2166                 return false;
2167             }
2168
2169             if ("unload" == eventName) {
2170                 unloadListeners[unloadListeners.length] =
2171                 [el, eventName, fn];
2172                 return true;
2173             }
2174
2175             var wrappedFn = function(e) {
2176                 return fn(Roo.lib.Event.getEvent(e));
2177             };
2178
2179             var li = [el, eventName, fn, wrappedFn];
2180
2181             var index = listeners.length;
2182             listeners[index] = li;
2183
2184             this.doAdd(el, eventName, wrappedFn, false);
2185             return true;
2186
2187         },
2188
2189
2190         removeListener: function(el, eventName, fn) {
2191             var i, len;
2192
2193             el = Roo.getDom(el);
2194
2195             if(!fn) {
2196                 return this.purgeElement(el, false, eventName);
2197             }
2198
2199
2200             if ("unload" == eventName) {
2201
2202                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2203                     var li = unloadListeners[i];
2204                     if (li &&
2205                         li[0] == el &&
2206                         li[1] == eventName &&
2207                         li[2] == fn) {
2208                         unloadListeners.splice(i, 1);
2209                         return true;
2210                     }
2211                 }
2212
2213                 return false;
2214             }
2215
2216             var cacheItem = null;
2217
2218
2219             var index = arguments[3];
2220
2221             if ("undefined" == typeof index) {
2222                 index = this._getCacheIndex(el, eventName, fn);
2223             }
2224
2225             if (index >= 0) {
2226                 cacheItem = listeners[index];
2227             }
2228
2229             if (!el || !cacheItem) {
2230                 return false;
2231             }
2232
2233             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2234
2235             delete listeners[index][this.WFN];
2236             delete listeners[index][this.FN];
2237             listeners.splice(index, 1);
2238
2239             return true;
2240
2241         },
2242
2243
2244         getTarget: function(ev, resolveTextNode) {
2245             ev = ev.browserEvent || ev;
2246             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2247             var t = ev.target || ev.srcElement;
2248             return this.resolveTextNode(t);
2249         },
2250
2251
2252         resolveTextNode: function(node) {
2253             if (Roo.isSafari && node && 3 == node.nodeType) {
2254                 return node.parentNode;
2255             } else {
2256                 return node;
2257             }
2258         },
2259
2260
2261         getPageX: function(ev) {
2262             ev = ev.browserEvent || ev;
2263             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2264             var x = ev.pageX;
2265             if (!x && 0 !== x) {
2266                 x = ev.clientX || 0;
2267
2268                 if (Roo.isIE) {
2269                     x += this.getScroll()[1];
2270                 }
2271             }
2272
2273             return x;
2274         },
2275
2276
2277         getPageY: function(ev) {
2278             ev = ev.browserEvent || ev;
2279             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2280             var y = ev.pageY;
2281             if (!y && 0 !== y) {
2282                 y = ev.clientY || 0;
2283
2284                 if (Roo.isIE) {
2285                     y += this.getScroll()[0];
2286                 }
2287             }
2288
2289
2290             return y;
2291         },
2292
2293
2294         getXY: function(ev) {
2295             ev = ev.browserEvent || ev;
2296             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2297             return [this.getPageX(ev), this.getPageY(ev)];
2298         },
2299
2300
2301         getRelatedTarget: function(ev) {
2302             ev = ev.browserEvent || ev;
2303             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2304             var t = ev.relatedTarget;
2305             if (!t) {
2306                 if (ev.type == "mouseout") {
2307                     t = ev.toElement;
2308                 } else if (ev.type == "mouseover") {
2309                     t = ev.fromElement;
2310                 }
2311             }
2312
2313             return this.resolveTextNode(t);
2314         },
2315
2316
2317         getTime: function(ev) {
2318             ev = ev.browserEvent || ev;
2319             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2320             if (!ev.time) {
2321                 var t = new Date().getTime();
2322                 try {
2323                     ev.time = t;
2324                 } catch(ex) {
2325                     this.lastError = ex;
2326                     return t;
2327                 }
2328             }
2329
2330             return ev.time;
2331         },
2332
2333
2334         stopEvent: function(ev) {
2335             this.stopPropagation(ev);
2336             this.preventDefault(ev);
2337         },
2338
2339
2340         stopPropagation: function(ev) {
2341             ev = ev.browserEvent || ev;
2342             if (ev.stopPropagation) {
2343                 ev.stopPropagation();
2344             } else {
2345                 ev.cancelBubble = true;
2346             }
2347         },
2348
2349
2350         preventDefault: function(ev) {
2351             ev = ev.browserEvent || ev;
2352             if(ev.preventDefault) {
2353                 ev.preventDefault();
2354             } else {
2355                 ev.returnValue = false;
2356             }
2357         },
2358
2359
2360         getEvent: function(e) {
2361             var ev = e || window.event;
2362             if (!ev) {
2363                 var c = this.getEvent.caller;
2364                 while (c) {
2365                     ev = c.arguments[0];
2366                     if (ev && Event == ev.constructor) {
2367                         break;
2368                     }
2369                     c = c.caller;
2370                 }
2371             }
2372             return ev;
2373         },
2374
2375
2376         getCharCode: function(ev) {
2377             ev = ev.browserEvent || ev;
2378             return ev.charCode || ev.keyCode || 0;
2379         },
2380
2381
2382         _getCacheIndex: function(el, eventName, fn) {
2383             for (var i = 0,len = listeners.length; i < len; ++i) {
2384                 var li = listeners[i];
2385                 if (li &&
2386                     li[this.FN] == fn &&
2387                     li[this.EL] == el &&
2388                     li[this.TYPE] == eventName) {
2389                     return i;
2390                 }
2391             }
2392
2393             return -1;
2394         },
2395
2396
2397         elCache: {},
2398
2399
2400         getEl: function(id) {
2401             return document.getElementById(id);
2402         },
2403
2404
2405         clearCache: function() {
2406         },
2407
2408
2409         _load: function(e) {
2410             loadComplete = true;
2411             var EU = Roo.lib.Event;
2412
2413
2414             if (Roo.isIE) {
2415                 EU.doRemove(window, "load", EU._load);
2416             }
2417         },
2418
2419
2420         _tryPreloadAttach: function() {
2421
2422             if (this.locked) {
2423                 return false;
2424             }
2425
2426             this.locked = true;
2427
2428
2429             var tryAgain = !loadComplete;
2430             if (!tryAgain) {
2431                 tryAgain = (retryCount > 0);
2432             }
2433
2434
2435             var notAvail = [];
2436             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2437                 var item = onAvailStack[i];
2438                 if (item) {
2439                     var el = this.getEl(item.id);
2440
2441                     if (el) {
2442                         if (!item.checkReady ||
2443                             loadComplete ||
2444                             el.nextSibling ||
2445                             (document && document.body)) {
2446
2447                             var scope = el;
2448                             if (item.override) {
2449                                 if (item.override === true) {
2450                                     scope = item.obj;
2451                                 } else {
2452                                     scope = item.override;
2453                                 }
2454                             }
2455                             item.fn.call(scope, item.obj);
2456                             onAvailStack[i] = null;
2457                         }
2458                     } else {
2459                         notAvail.push(item);
2460                     }
2461                 }
2462             }
2463
2464             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2465
2466             if (tryAgain) {
2467
2468                 this.startInterval();
2469             } else {
2470                 clearInterval(this._interval);
2471                 this._interval = null;
2472             }
2473
2474             this.locked = false;
2475
2476             return true;
2477
2478         },
2479
2480
2481         purgeElement: function(el, recurse, eventName) {
2482             var elListeners = this.getListeners(el, eventName);
2483             if (elListeners) {
2484                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2485                     var l = elListeners[i];
2486                     this.removeListener(el, l.type, l.fn);
2487                 }
2488             }
2489
2490             if (recurse && el && el.childNodes) {
2491                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2492                     this.purgeElement(el.childNodes[i], recurse, eventName);
2493                 }
2494             }
2495         },
2496
2497
2498         getListeners: function(el, eventName) {
2499             var results = [], searchLists;
2500             if (!eventName) {
2501                 searchLists = [listeners, unloadListeners];
2502             } else if (eventName == "unload") {
2503                 searchLists = [unloadListeners];
2504             } else {
2505                 searchLists = [listeners];
2506             }
2507
2508             for (var j = 0; j < searchLists.length; ++j) {
2509                 var searchList = searchLists[j];
2510                 if (searchList && searchList.length > 0) {
2511                     for (var i = 0,len = searchList.length; i < len; ++i) {
2512                         var l = searchList[i];
2513                         if (l && l[this.EL] === el &&
2514                             (!eventName || eventName === l[this.TYPE])) {
2515                             results.push({
2516                                 type:   l[this.TYPE],
2517                                 fn:     l[this.FN],
2518                                 obj:    l[this.OBJ],
2519                                 adjust: l[this.ADJ_SCOPE],
2520                                 index:  i
2521                             });
2522                         }
2523                     }
2524                 }
2525             }
2526
2527             return (results.length) ? results : null;
2528         },
2529
2530
2531         _unload: function(e) {
2532
2533             var EU = Roo.lib.Event, i, j, l, len, index;
2534
2535             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2536                 l = unloadListeners[i];
2537                 if (l) {
2538                     var scope = window;
2539                     if (l[EU.ADJ_SCOPE]) {
2540                         if (l[EU.ADJ_SCOPE] === true) {
2541                             scope = l[EU.OBJ];
2542                         } else {
2543                             scope = l[EU.ADJ_SCOPE];
2544                         }
2545                     }
2546                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2547                     unloadListeners[i] = null;
2548                     l = null;
2549                     scope = null;
2550                 }
2551             }
2552
2553             unloadListeners = null;
2554
2555             if (listeners && listeners.length > 0) {
2556                 j = listeners.length;
2557                 while (j) {
2558                     index = j - 1;
2559                     l = listeners[index];
2560                     if (l) {
2561                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2562                                 l[EU.FN], index);
2563                     }
2564                     j = j - 1;
2565                 }
2566                 l = null;
2567
2568                 EU.clearCache();
2569             }
2570
2571             EU.doRemove(window, "unload", EU._unload);
2572
2573         },
2574
2575
2576         getScroll: function() {
2577             var dd = document.documentElement, db = document.body;
2578             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2579                 return [dd.scrollTop, dd.scrollLeft];
2580             } else if (db) {
2581                 return [db.scrollTop, db.scrollLeft];
2582             } else {
2583                 return [0, 0];
2584             }
2585         },
2586
2587
2588         doAdd: function () {
2589             if (window.addEventListener) {
2590                 return function(el, eventName, fn, capture) {
2591                     el.addEventListener(eventName, fn, (capture));
2592                 };
2593             } else if (window.attachEvent) {
2594                 return function(el, eventName, fn, capture) {
2595                     el.attachEvent("on" + eventName, fn);
2596                 };
2597             } else {
2598                 return function() {
2599                 };
2600             }
2601         }(),
2602
2603
2604         doRemove: function() {
2605             if (window.removeEventListener) {
2606                 return function (el, eventName, fn, capture) {
2607                     el.removeEventListener(eventName, fn, (capture));
2608                 };
2609             } else if (window.detachEvent) {
2610                 return function (el, eventName, fn) {
2611                     el.detachEvent("on" + eventName, fn);
2612                 };
2613             } else {
2614                 return function() {
2615                 };
2616             }
2617         }()
2618     };
2619     
2620 }();
2621 (function() {     
2622    
2623     var E = Roo.lib.Event;
2624     E.on = E.addListener;
2625     E.un = E.removeListener;
2626
2627     if (document && document.body) {
2628         E._load();
2629     } else {
2630         E.doAdd(window, "load", E._load);
2631     }
2632     E.doAdd(window, "unload", E._unload);
2633     E._tryPreloadAttach();
2634 })();
2635
2636  
2637
2638 (function() {
2639     /**
2640      * @class Roo.lib.Ajax
2641      *
2642      * provide a simple Ajax request utility functions
2643      * 
2644      * Portions of this file are based on pieces of Yahoo User Interface Library
2645     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2646     * YUI licensed under the BSD License:
2647     * http://developer.yahoo.net/yui/license.txt
2648     * <script type="text/javascript">
2649     *
2650      *
2651      */
2652     Roo.lib.Ajax = {
2653         /**
2654          * @static 
2655          */
2656         request : function(method, uri, cb, data, options) {
2657             if(options){
2658                 var hs = options.headers;
2659                 if(hs){
2660                     for(var h in hs){
2661                         if(hs.hasOwnProperty(h)){
2662                             this.initHeader(h, hs[h], false);
2663                         }
2664                     }
2665                 }
2666                 if(options.xmlData){
2667                     this.initHeader('Content-Type', 'text/xml', false);
2668                     method = 'POST';
2669                     data = options.xmlData;
2670                 }
2671             }
2672
2673             return this.asyncRequest(method, uri, cb, data);
2674         },
2675         /**
2676          * serialize a form
2677          *
2678          * @static
2679          * @param {DomForm} form element
2680          * @return {String} urlencode form output.
2681          */
2682         serializeForm : function(form) {
2683             if(typeof form == 'string') {
2684                 form = (document.getElementById(form) || document.forms[form]);
2685             }
2686
2687             var el, name, val, disabled, data = '', hasSubmit = false;
2688             for (var i = 0; i < form.elements.length; i++) {
2689                 el = form.elements[i];
2690                 disabled = form.elements[i].disabled;
2691                 name = form.elements[i].name;
2692                 val = form.elements[i].value;
2693
2694                 if (!disabled && name){
2695                     switch (el.type)
2696                             {
2697                         case 'select-one':
2698                         case 'select-multiple':
2699                             for (var j = 0; j < el.options.length; j++) {
2700                                 if (el.options[j].selected) {
2701                                     if (Roo.isIE) {
2702                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2703                                     }
2704                                     else {
2705                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2706                                     }
2707                                 }
2708                             }
2709                             break;
2710                         case 'radio':
2711                         case 'checkbox':
2712                             if (el.checked) {
2713                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2714                             }
2715                             break;
2716                         case 'file':
2717
2718                         case undefined:
2719
2720                         case 'reset':
2721
2722                         case 'button':
2723
2724                             break;
2725                         case 'submit':
2726                             if(hasSubmit == false) {
2727                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2728                                 hasSubmit = true;
2729                             }
2730                             break;
2731                         default:
2732                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2733                             break;
2734                     }
2735                 }
2736             }
2737             data = data.substr(0, data.length - 1);
2738             return data;
2739         },
2740
2741         headers:{},
2742
2743         hasHeaders:false,
2744
2745         useDefaultHeader:true,
2746
2747         defaultPostHeader:'application/x-www-form-urlencoded',
2748
2749         useDefaultXhrHeader:true,
2750
2751         defaultXhrHeader:'XMLHttpRequest',
2752
2753         hasDefaultHeaders:true,
2754
2755         defaultHeaders:{},
2756
2757         poll:{},
2758
2759         timeout:{},
2760
2761         pollInterval:50,
2762
2763         transactionId:0,
2764
2765         setProgId:function(id)
2766         {
2767             this.activeX.unshift(id);
2768         },
2769
2770         setDefaultPostHeader:function(b)
2771         {
2772             this.useDefaultHeader = b;
2773         },
2774
2775         setDefaultXhrHeader:function(b)
2776         {
2777             this.useDefaultXhrHeader = b;
2778         },
2779
2780         setPollingInterval:function(i)
2781         {
2782             if (typeof i == 'number' && isFinite(i)) {
2783                 this.pollInterval = i;
2784             }
2785         },
2786
2787         createXhrObject:function(transactionId)
2788         {
2789             var obj,http;
2790             try
2791             {
2792
2793                 http = new XMLHttpRequest();
2794
2795                 obj = { conn:http, tId:transactionId };
2796             }
2797             catch(e)
2798             {
2799                 for (var i = 0; i < this.activeX.length; ++i) {
2800                     try
2801                     {
2802
2803                         http = new ActiveXObject(this.activeX[i]);
2804
2805                         obj = { conn:http, tId:transactionId };
2806                         break;
2807                     }
2808                     catch(e) {
2809                     }
2810                 }
2811             }
2812             finally
2813             {
2814                 return obj;
2815             }
2816         },
2817
2818         getConnectionObject:function()
2819         {
2820             var o;
2821             var tId = this.transactionId;
2822
2823             try
2824             {
2825                 o = this.createXhrObject(tId);
2826                 if (o) {
2827                     this.transactionId++;
2828                 }
2829             }
2830             catch(e) {
2831             }
2832             finally
2833             {
2834                 return o;
2835             }
2836         },
2837
2838         asyncRequest:function(method, uri, callback, postData)
2839         {
2840             var o = this.getConnectionObject();
2841
2842             if (!o) {
2843                 return null;
2844             }
2845             else {
2846                 o.conn.open(method, uri, true);
2847
2848                 if (this.useDefaultXhrHeader) {
2849                     if (!this.defaultHeaders['X-Requested-With']) {
2850                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2851                     }
2852                 }
2853
2854                 if(postData && this.useDefaultHeader){
2855                     this.initHeader('Content-Type', this.defaultPostHeader);
2856                 }
2857
2858                  if (this.hasDefaultHeaders || this.hasHeaders) {
2859                     this.setHeader(o);
2860                 }
2861
2862                 this.handleReadyState(o, callback);
2863                 o.conn.send(postData || null);
2864
2865                 return o;
2866             }
2867         },
2868
2869         handleReadyState:function(o, callback)
2870         {
2871             var oConn = this;
2872
2873             if (callback && callback.timeout) {
2874                 
2875                 this.timeout[o.tId] = window.setTimeout(function() {
2876                     oConn.abort(o, callback, true);
2877                 }, callback.timeout);
2878             }
2879
2880             this.poll[o.tId] = window.setInterval(
2881                     function() {
2882                         if (o.conn && o.conn.readyState == 4) {
2883                             window.clearInterval(oConn.poll[o.tId]);
2884                             delete oConn.poll[o.tId];
2885
2886                             if(callback && callback.timeout) {
2887                                 window.clearTimeout(oConn.timeout[o.tId]);
2888                                 delete oConn.timeout[o.tId];
2889                             }
2890
2891                             oConn.handleTransactionResponse(o, callback);
2892                         }
2893                     }
2894                     , this.pollInterval);
2895         },
2896
2897         handleTransactionResponse:function(o, callback, isAbort)
2898         {
2899
2900             if (!callback) {
2901                 this.releaseObject(o);
2902                 return;
2903             }
2904
2905             var httpStatus, responseObject;
2906
2907             try
2908             {
2909                 if (o.conn.status !== undefined && o.conn.status != 0) {
2910                     httpStatus = o.conn.status;
2911                 }
2912                 else {
2913                     httpStatus = 13030;
2914                 }
2915             }
2916             catch(e) {
2917
2918
2919                 httpStatus = 13030;
2920             }
2921
2922             if (httpStatus >= 200 && httpStatus < 300) {
2923                 responseObject = this.createResponseObject(o, callback.argument);
2924                 if (callback.success) {
2925                     if (!callback.scope) {
2926                         callback.success(responseObject);
2927                     }
2928                     else {
2929
2930
2931                         callback.success.apply(callback.scope, [responseObject]);
2932                     }
2933                 }
2934             }
2935             else {
2936                 switch (httpStatus) {
2937
2938                     case 12002:
2939                     case 12029:
2940                     case 12030:
2941                     case 12031:
2942                     case 12152:
2943                     case 13030:
2944                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2945                         if (callback.failure) {
2946                             if (!callback.scope) {
2947                                 callback.failure(responseObject);
2948                             }
2949                             else {
2950                                 callback.failure.apply(callback.scope, [responseObject]);
2951                             }
2952                         }
2953                         break;
2954                     default:
2955                         responseObject = this.createResponseObject(o, callback.argument);
2956                         if (callback.failure) {
2957                             if (!callback.scope) {
2958                                 callback.failure(responseObject);
2959                             }
2960                             else {
2961                                 callback.failure.apply(callback.scope, [responseObject]);
2962                             }
2963                         }
2964                 }
2965             }
2966
2967             this.releaseObject(o);
2968             responseObject = null;
2969         },
2970
2971         createResponseObject:function(o, callbackArg)
2972         {
2973             var obj = {};
2974             var headerObj = {};
2975
2976             try
2977             {
2978                 var headerStr = o.conn.getAllResponseHeaders();
2979                 var header = headerStr.split('\n');
2980                 for (var i = 0; i < header.length; i++) {
2981                     var delimitPos = header[i].indexOf(':');
2982                     if (delimitPos != -1) {
2983                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2984                     }
2985                 }
2986             }
2987             catch(e) {
2988             }
2989
2990             obj.tId = o.tId;
2991             obj.status = o.conn.status;
2992             obj.statusText = o.conn.statusText;
2993             obj.getResponseHeader = headerObj;
2994             obj.getAllResponseHeaders = headerStr;
2995             obj.responseText = o.conn.responseText;
2996             obj.responseXML = o.conn.responseXML;
2997
2998             if (typeof callbackArg !== undefined) {
2999                 obj.argument = callbackArg;
3000             }
3001
3002             return obj;
3003         },
3004
3005         createExceptionObject:function(tId, callbackArg, isAbort)
3006         {
3007             var COMM_CODE = 0;
3008             var COMM_ERROR = 'communication failure';
3009             var ABORT_CODE = -1;
3010             var ABORT_ERROR = 'transaction aborted';
3011
3012             var obj = {};
3013
3014             obj.tId = tId;
3015             if (isAbort) {
3016                 obj.status = ABORT_CODE;
3017                 obj.statusText = ABORT_ERROR;
3018             }
3019             else {
3020                 obj.status = COMM_CODE;
3021                 obj.statusText = COMM_ERROR;
3022             }
3023
3024             if (callbackArg) {
3025                 obj.argument = callbackArg;
3026             }
3027
3028             return obj;
3029         },
3030
3031         initHeader:function(label, value, isDefault)
3032         {
3033             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3034
3035             if (headerObj[label] === undefined) {
3036                 headerObj[label] = value;
3037             }
3038             else {
3039
3040
3041                 headerObj[label] = value + "," + headerObj[label];
3042             }
3043
3044             if (isDefault) {
3045                 this.hasDefaultHeaders = true;
3046             }
3047             else {
3048                 this.hasHeaders = true;
3049             }
3050         },
3051
3052
3053         setHeader:function(o)
3054         {
3055             if (this.hasDefaultHeaders) {
3056                 for (var prop in this.defaultHeaders) {
3057                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3058                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3059                     }
3060                 }
3061             }
3062
3063             if (this.hasHeaders) {
3064                 for (var prop in this.headers) {
3065                     if (this.headers.hasOwnProperty(prop)) {
3066                         o.conn.setRequestHeader(prop, this.headers[prop]);
3067                     }
3068                 }
3069                 this.headers = {};
3070                 this.hasHeaders = false;
3071             }
3072         },
3073
3074         resetDefaultHeaders:function() {
3075             delete this.defaultHeaders;
3076             this.defaultHeaders = {};
3077             this.hasDefaultHeaders = false;
3078         },
3079
3080         abort:function(o, callback, isTimeout)
3081         {
3082             if(this.isCallInProgress(o)) {
3083                 o.conn.abort();
3084                 window.clearInterval(this.poll[o.tId]);
3085                 delete this.poll[o.tId];
3086                 if (isTimeout) {
3087                     delete this.timeout[o.tId];
3088                 }
3089
3090                 this.handleTransactionResponse(o, callback, true);
3091
3092                 return true;
3093             }
3094             else {
3095                 return false;
3096             }
3097         },
3098
3099
3100         isCallInProgress:function(o)
3101         {
3102             if (o && o.conn) {
3103                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3104             }
3105             else {
3106
3107                 return false;
3108             }
3109         },
3110
3111
3112         releaseObject:function(o)
3113         {
3114
3115             o.conn = null;
3116
3117             o = null;
3118         },
3119
3120         activeX:[
3121         'MSXML2.XMLHTTP.3.0',
3122         'MSXML2.XMLHTTP',
3123         'Microsoft.XMLHTTP'
3124         ]
3125
3126
3127     };
3128 })();/*
3129  * Portions of this file are based on pieces of Yahoo User Interface Library
3130  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3131  * YUI licensed under the BSD License:
3132  * http://developer.yahoo.net/yui/license.txt
3133  * <script type="text/javascript">
3134  *
3135  */
3136
3137 Roo.lib.Region = function(t, r, b, l) {
3138     this.top = t;
3139     this[1] = t;
3140     this.right = r;
3141     this.bottom = b;
3142     this.left = l;
3143     this[0] = l;
3144 };
3145
3146
3147 Roo.lib.Region.prototype = {
3148     contains : function(region) {
3149         return ( region.left >= this.left &&
3150                  region.right <= this.right &&
3151                  region.top >= this.top &&
3152                  region.bottom <= this.bottom    );
3153
3154     },
3155
3156     getArea : function() {
3157         return ( (this.bottom - this.top) * (this.right - this.left) );
3158     },
3159
3160     intersect : function(region) {
3161         var t = Math.max(this.top, region.top);
3162         var r = Math.min(this.right, region.right);
3163         var b = Math.min(this.bottom, region.bottom);
3164         var l = Math.max(this.left, region.left);
3165
3166         if (b >= t && r >= l) {
3167             return new Roo.lib.Region(t, r, b, l);
3168         } else {
3169             return null;
3170         }
3171     },
3172     union : function(region) {
3173         var t = Math.min(this.top, region.top);
3174         var r = Math.max(this.right, region.right);
3175         var b = Math.max(this.bottom, region.bottom);
3176         var l = Math.min(this.left, region.left);
3177
3178         return new Roo.lib.Region(t, r, b, l);
3179     },
3180
3181     adjust : function(t, l, b, r) {
3182         this.top += t;
3183         this.left += l;
3184         this.right += r;
3185         this.bottom += b;
3186         return this;
3187     }
3188 };
3189
3190 Roo.lib.Region.getRegion = function(el) {
3191     var p = Roo.lib.Dom.getXY(el);
3192
3193     var t = p[1];
3194     var r = p[0] + el.offsetWidth;
3195     var b = p[1] + el.offsetHeight;
3196     var l = p[0];
3197
3198     return new Roo.lib.Region(t, r, b, l);
3199 };
3200 /*
3201  * Portions of this file are based on pieces of Yahoo User Interface Library
3202  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3203  * YUI licensed under the BSD License:
3204  * http://developer.yahoo.net/yui/license.txt
3205  * <script type="text/javascript">
3206  *
3207  */
3208 //@@dep Roo.lib.Region
3209
3210
3211 Roo.lib.Point = function(x, y) {
3212     if (x instanceof Array) {
3213         y = x[1];
3214         x = x[0];
3215     }
3216     this.x = this.right = this.left = this[0] = x;
3217     this.y = this.top = this.bottom = this[1] = y;
3218 };
3219
3220 Roo.lib.Point.prototype = new Roo.lib.Region();
3221 /*
3222  * Portions of this file are based on pieces of Yahoo User Interface Library
3223  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3224  * YUI licensed under the BSD License:
3225  * http://developer.yahoo.net/yui/license.txt
3226  * <script type="text/javascript">
3227  *
3228  */
3229  
3230 (function() {   
3231
3232     Roo.lib.Anim = {
3233         scroll : function(el, args, duration, easing, cb, scope) {
3234             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3235         },
3236
3237         motion : function(el, args, duration, easing, cb, scope) {
3238             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3239         },
3240
3241         color : function(el, args, duration, easing, cb, scope) {
3242             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3243         },
3244
3245         run : function(el, args, duration, easing, cb, scope, type) {
3246             type = type || Roo.lib.AnimBase;
3247             if (typeof easing == "string") {
3248                 easing = Roo.lib.Easing[easing];
3249             }
3250             var anim = new type(el, args, duration, easing);
3251             anim.animateX(function() {
3252                 Roo.callback(cb, scope);
3253             });
3254             return anim;
3255         }
3256     };
3257 })();/*
3258  * Portions of this file are based on pieces of Yahoo User Interface Library
3259  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3260  * YUI licensed under the BSD License:
3261  * http://developer.yahoo.net/yui/license.txt
3262  * <script type="text/javascript">
3263  *
3264  */
3265
3266 (function() {    
3267     var libFlyweight;
3268     
3269     function fly(el) {
3270         if (!libFlyweight) {
3271             libFlyweight = new Roo.Element.Flyweight();
3272         }
3273         libFlyweight.dom = el;
3274         return libFlyweight;
3275     }
3276
3277     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3278     
3279    
3280     
3281     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3282         if (el) {
3283             this.init(el, attributes, duration, method);
3284         }
3285     };
3286
3287     Roo.lib.AnimBase.fly = fly;
3288     
3289     
3290     
3291     Roo.lib.AnimBase.prototype = {
3292
3293         toString: function() {
3294             var el = this.getEl();
3295             var id = el.id || el.tagName;
3296             return ("Anim " + id);
3297         },
3298
3299         patterns: {
3300             noNegatives:        /width|height|opacity|padding/i,
3301             offsetAttribute:  /^((width|height)|(top|left))$/,
3302             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3303             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3304         },
3305
3306
3307         doMethod: function(attr, start, end) {
3308             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3309         },
3310
3311
3312         setAttribute: function(attr, val, unit) {
3313             if (this.patterns.noNegatives.test(attr)) {
3314                 val = (val > 0) ? val : 0;
3315             }
3316
3317             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3318         },
3319
3320
3321         getAttribute: function(attr) {
3322             var el = this.getEl();
3323             var val = fly(el).getStyle(attr);
3324
3325             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3326                 return parseFloat(val);
3327             }
3328
3329             var a = this.patterns.offsetAttribute.exec(attr) || [];
3330             var pos = !!( a[3] );
3331             var box = !!( a[2] );
3332
3333
3334             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3335                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3336             } else {
3337                 val = 0;
3338             }
3339
3340             return val;
3341         },
3342
3343
3344         getDefaultUnit: function(attr) {
3345             if (this.patterns.defaultUnit.test(attr)) {
3346                 return 'px';
3347             }
3348
3349             return '';
3350         },
3351
3352         animateX : function(callback, scope) {
3353             var f = function() {
3354                 this.onComplete.removeListener(f);
3355                 if (typeof callback == "function") {
3356                     callback.call(scope || this, this);
3357                 }
3358             };
3359             this.onComplete.addListener(f, this);
3360             this.animate();
3361         },
3362
3363
3364         setRuntimeAttribute: function(attr) {
3365             var start;
3366             var end;
3367             var attributes = this.attributes;
3368
3369             this.runtimeAttributes[attr] = {};
3370
3371             var isset = function(prop) {
3372                 return (typeof prop !== 'undefined');
3373             };
3374
3375             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3376                 return false;
3377             }
3378
3379             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3380
3381
3382             if (isset(attributes[attr]['to'])) {
3383                 end = attributes[attr]['to'];
3384             } else if (isset(attributes[attr]['by'])) {
3385                 if (start.constructor == Array) {
3386                     end = [];
3387                     for (var i = 0, len = start.length; i < len; ++i) {
3388                         end[i] = start[i] + attributes[attr]['by'][i];
3389                     }
3390                 } else {
3391                     end = start + attributes[attr]['by'];
3392                 }
3393             }
3394
3395             this.runtimeAttributes[attr].start = start;
3396             this.runtimeAttributes[attr].end = end;
3397
3398
3399             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3400         },
3401
3402
3403         init: function(el, attributes, duration, method) {
3404
3405             var isAnimated = false;
3406
3407
3408             var startTime = null;
3409
3410
3411             var actualFrames = 0;
3412
3413
3414             el = Roo.getDom(el);
3415
3416
3417             this.attributes = attributes || {};
3418
3419
3420             this.duration = duration || 1;
3421
3422
3423             this.method = method || Roo.lib.Easing.easeNone;
3424
3425
3426             this.useSeconds = true;
3427
3428
3429             this.currentFrame = 0;
3430
3431
3432             this.totalFrames = Roo.lib.AnimMgr.fps;
3433
3434
3435             this.getEl = function() {
3436                 return el;
3437             };
3438
3439
3440             this.isAnimated = function() {
3441                 return isAnimated;
3442             };
3443
3444
3445             this.getStartTime = function() {
3446                 return startTime;
3447             };
3448
3449             this.runtimeAttributes = {};
3450
3451
3452             this.animate = function() {
3453                 if (this.isAnimated()) {
3454                     return false;
3455                 }
3456
3457                 this.currentFrame = 0;
3458
3459                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3460
3461                 Roo.lib.AnimMgr.registerElement(this);
3462             };
3463
3464
3465             this.stop = function(finish) {
3466                 if (finish) {
3467                     this.currentFrame = this.totalFrames;
3468                     this._onTween.fire();
3469                 }
3470                 Roo.lib.AnimMgr.stop(this);
3471             };
3472
3473             var onStart = function() {
3474                 this.onStart.fire();
3475
3476                 this.runtimeAttributes = {};
3477                 for (var attr in this.attributes) {
3478                     this.setRuntimeAttribute(attr);
3479                 }
3480
3481                 isAnimated = true;
3482                 actualFrames = 0;
3483                 startTime = new Date();
3484             };
3485
3486
3487             var onTween = function() {
3488                 var data = {
3489                     duration: new Date() - this.getStartTime(),
3490                     currentFrame: this.currentFrame
3491                 };
3492
3493                 data.toString = function() {
3494                     return (
3495                             'duration: ' + data.duration +
3496                             ', currentFrame: ' + data.currentFrame
3497                             );
3498                 };
3499
3500                 this.onTween.fire(data);
3501
3502                 var runtimeAttributes = this.runtimeAttributes;
3503
3504                 for (var attr in runtimeAttributes) {
3505                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3506                 }
3507
3508                 actualFrames += 1;
3509             };
3510
3511             var onComplete = function() {
3512                 var actual_duration = (new Date() - startTime) / 1000 ;
3513
3514                 var data = {
3515                     duration: actual_duration,
3516                     frames: actualFrames,
3517                     fps: actualFrames / actual_duration
3518                 };
3519
3520                 data.toString = function() {
3521                     return (
3522                             'duration: ' + data.duration +
3523                             ', frames: ' + data.frames +
3524                             ', fps: ' + data.fps
3525                             );
3526                 };
3527
3528                 isAnimated = false;
3529                 actualFrames = 0;
3530                 this.onComplete.fire(data);
3531             };
3532
3533
3534             this._onStart = new Roo.util.Event(this);
3535             this.onStart = new Roo.util.Event(this);
3536             this.onTween = new Roo.util.Event(this);
3537             this._onTween = new Roo.util.Event(this);
3538             this.onComplete = new Roo.util.Event(this);
3539             this._onComplete = new Roo.util.Event(this);
3540             this._onStart.addListener(onStart);
3541             this._onTween.addListener(onTween);
3542             this._onComplete.addListener(onComplete);
3543         }
3544     };
3545 })();
3546 /*
3547  * Portions of this file are based on pieces of Yahoo User Interface Library
3548  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3549  * YUI licensed under the BSD License:
3550  * http://developer.yahoo.net/yui/license.txt
3551  * <script type="text/javascript">
3552  *
3553  */
3554
3555 Roo.lib.AnimMgr = new function() {
3556
3557     var thread = null;
3558
3559
3560     var queue = [];
3561
3562
3563     var tweenCount = 0;
3564
3565
3566     this.fps = 1000;
3567
3568
3569     this.delay = 1;
3570
3571
3572     this.registerElement = function(tween) {
3573         queue[queue.length] = tween;
3574         tweenCount += 1;
3575         tween._onStart.fire();
3576         this.start();
3577     };
3578
3579
3580     this.unRegister = function(tween, index) {
3581         tween._onComplete.fire();
3582         index = index || getIndex(tween);
3583         if (index != -1) {
3584             queue.splice(index, 1);
3585         }
3586
3587         tweenCount -= 1;
3588         if (tweenCount <= 0) {
3589             this.stop();
3590         }
3591     };
3592
3593
3594     this.start = function() {
3595         if (thread === null) {
3596             thread = setInterval(this.run, this.delay);
3597         }
3598     };
3599
3600
3601     this.stop = function(tween) {
3602         if (!tween) {
3603             clearInterval(thread);
3604
3605             for (var i = 0, len = queue.length; i < len; ++i) {
3606                 if (queue[0].isAnimated()) {
3607                     this.unRegister(queue[0], 0);
3608                 }
3609             }
3610
3611             queue = [];
3612             thread = null;
3613             tweenCount = 0;
3614         }
3615         else {
3616             this.unRegister(tween);
3617         }
3618     };
3619
3620
3621     this.run = function() {
3622         for (var i = 0, len = queue.length; i < len; ++i) {
3623             var tween = queue[i];
3624             if (!tween || !tween.isAnimated()) {
3625                 continue;
3626             }
3627
3628             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3629             {
3630                 tween.currentFrame += 1;
3631
3632                 if (tween.useSeconds) {
3633                     correctFrame(tween);
3634                 }
3635                 tween._onTween.fire();
3636             }
3637             else {
3638                 Roo.lib.AnimMgr.stop(tween, i);
3639             }
3640         }
3641     };
3642
3643     var getIndex = function(anim) {
3644         for (var i = 0, len = queue.length; i < len; ++i) {
3645             if (queue[i] == anim) {
3646                 return i;
3647             }
3648         }
3649         return -1;
3650     };
3651
3652
3653     var correctFrame = function(tween) {
3654         var frames = tween.totalFrames;
3655         var frame = tween.currentFrame;
3656         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3657         var elapsed = (new Date() - tween.getStartTime());
3658         var tweak = 0;
3659
3660         if (elapsed < tween.duration * 1000) {
3661             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3662         } else {
3663             tweak = frames - (frame + 1);
3664         }
3665         if (tweak > 0 && isFinite(tweak)) {
3666             if (tween.currentFrame + tweak >= frames) {
3667                 tweak = frames - (frame + 1);
3668             }
3669
3670             tween.currentFrame += tweak;
3671         }
3672     };
3673 };
3674
3675     /*
3676  * Portions of this file are based on pieces of Yahoo User Interface Library
3677  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3678  * YUI licensed under the BSD License:
3679  * http://developer.yahoo.net/yui/license.txt
3680  * <script type="text/javascript">
3681  *
3682  */
3683 Roo.lib.Bezier = new function() {
3684
3685         this.getPosition = function(points, t) {
3686             var n = points.length;
3687             var tmp = [];
3688
3689             for (var i = 0; i < n; ++i) {
3690                 tmp[i] = [points[i][0], points[i][1]];
3691             }
3692
3693             for (var j = 1; j < n; ++j) {
3694                 for (i = 0; i < n - j; ++i) {
3695                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3696                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3697                 }
3698             }
3699
3700             return [ tmp[0][0], tmp[0][1] ];
3701
3702         };
3703     }; 
3704
3705 /**
3706  * @class Roo.lib.Color
3707  * @constructor
3708  * An abstract Color implementation. Concrete Color implementations should use
3709  * an instance of this function as their prototype, and implement the getRGB and
3710  * getHSL functions. getRGB should return an object representing the RGB
3711  * components of this Color, with the red, green, and blue components in the
3712  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3713  * return an object representing the HSL components of this Color, with the hue
3714  * component in the range [0,360), the saturation and lightness components in
3715  * the range [0,100], and the alpha component in the range [0,1].
3716  *
3717  *
3718  * Color.js
3719  *
3720  * Functions for Color handling and processing.
3721  *
3722  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3723  *
3724  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3725  * rights to this program, with the intention of it becoming part of the public
3726  * domain. Because this program is released into the public domain, it comes with
3727  * no warranty either expressed or implied, to the extent permitted by law.
3728  * 
3729  * For more free and public domain JavaScript code by the same author, visit:
3730  * http://www.safalra.com/web-design/javascript/
3731  * 
3732  */
3733 Roo.lib.Color = function() { }
3734
3735
3736 Roo.apply(Roo.lib.Color.prototype, {
3737   
3738   rgb : null,
3739   hsv : null,
3740   hsl : null,
3741   
3742   /**
3743    * getIntegerRGB
3744    * @return {Object} an object representing the RGBA components of this Color. The red,
3745    * green, and blue components are converted to integers in the range [0,255].
3746    * The alpha is a value in the range [0,1].
3747    */
3748   getIntegerRGB : function(){
3749
3750     // get the RGB components of this Color
3751     var rgb = this.getRGB();
3752
3753     // return the integer components
3754     return {
3755       'r' : Math.round(rgb.r),
3756       'g' : Math.round(rgb.g),
3757       'b' : Math.round(rgb.b),
3758       'a' : rgb.a
3759     };
3760
3761   },
3762
3763   /**
3764    * getPercentageRGB
3765    * @return {Object} an object representing the RGBA components of this Color. The red,
3766    * green, and blue components are converted to numbers in the range [0,100].
3767    * The alpha is a value in the range [0,1].
3768    */
3769   getPercentageRGB : function(){
3770
3771     // get the RGB components of this Color
3772     var rgb = this.getRGB();
3773
3774     // return the percentage components
3775     return {
3776       'r' : 100 * rgb.r / 255,
3777       'g' : 100 * rgb.g / 255,
3778       'b' : 100 * rgb.b / 255,
3779       'a' : rgb.a
3780     };
3781
3782   },
3783
3784   /**
3785    * getCSSHexadecimalRGB
3786    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3787    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3788    * are two-digit hexadecimal numbers.
3789    */
3790   getCSSHexadecimalRGB : function()
3791   {
3792
3793     // get the integer RGB components
3794     var rgb = this.getIntegerRGB();
3795
3796     // determine the hexadecimal equivalents
3797     var r16 = rgb.r.toString(16);
3798     var g16 = rgb.g.toString(16);
3799     var b16 = rgb.b.toString(16);
3800
3801     // return the CSS RGB Color value
3802     return '#'
3803         + (r16.length == 2 ? r16 : '0' + r16)
3804         + (g16.length == 2 ? g16 : '0' + g16)
3805         + (b16.length == 2 ? b16 : '0' + b16);
3806
3807   },
3808
3809   /**
3810    * getCSSIntegerRGB
3811    * @return {String} a string representing this Color as a CSS integer RGB Color
3812    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3813    * are integers in the range [0,255].
3814    */
3815   getCSSIntegerRGB : function(){
3816
3817     // get the integer RGB components
3818     var rgb = this.getIntegerRGB();
3819
3820     // return the CSS RGB Color value
3821     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3822
3823   },
3824
3825   /**
3826    * getCSSIntegerRGBA
3827    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3828    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3829    * b are integers in the range [0,255] and a is in the range [0,1].
3830    */
3831   getCSSIntegerRGBA : function(){
3832
3833     // get the integer RGB components
3834     var rgb = this.getIntegerRGB();
3835
3836     // return the CSS integer RGBA Color value
3837     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3838
3839   },
3840
3841   /**
3842    * getCSSPercentageRGB
3843    * @return {String} a string representing this Color as a CSS percentage RGB Color
3844    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3845    * b are in the range [0,100].
3846    */
3847   getCSSPercentageRGB : function(){
3848
3849     // get the percentage RGB components
3850     var rgb = this.getPercentageRGB();
3851
3852     // return the CSS RGB Color value
3853     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3854
3855   },
3856
3857   /**
3858    * getCSSPercentageRGBA
3859    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3860    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3861    * and b are in the range [0,100] and a is in the range [0,1].
3862    */
3863   getCSSPercentageRGBA : function(){
3864
3865     // get the percentage RGB components
3866     var rgb = this.getPercentageRGB();
3867
3868     // return the CSS percentage RGBA Color value
3869     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3870
3871   },
3872
3873   /**
3874    * getCSSHSL
3875    * @return {String} a string representing this Color as a CSS HSL Color value - that
3876    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3877    * s and l are in the range [0,100].
3878    */
3879   getCSSHSL : function(){
3880
3881     // get the HSL components
3882     var hsl = this.getHSL();
3883
3884     // return the CSS HSL Color value
3885     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3886
3887   },
3888
3889   /**
3890    * getCSSHSLA
3891    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3892    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3893    * s and l are in the range [0,100], and a is in the range [0,1].
3894    */
3895   getCSSHSLA : function(){
3896
3897     // get the HSL components
3898     var hsl = this.getHSL();
3899
3900     // return the CSS HSL Color value
3901     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3902
3903   },
3904
3905   /**
3906    * Sets the Color of the specified node to this Color. This functions sets
3907    * the CSS 'color' property for the node. The parameter is:
3908    * 
3909    * @param {DomElement} node - the node whose Color should be set
3910    */
3911   setNodeColor : function(node){
3912
3913     // set the Color of the node
3914     node.style.color = this.getCSSHexadecimalRGB();
3915
3916   },
3917
3918   /**
3919    * Sets the background Color of the specified node to this Color. This
3920    * functions sets the CSS 'background-color' property for the node. The
3921    * parameter is:
3922    *
3923    * @param {DomElement} node - the node whose background Color should be set
3924    */
3925   setNodeBackgroundColor : function(node){
3926
3927     // set the background Color of the node
3928     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3929
3930   },
3931   // convert between formats..
3932   toRGB: function()
3933   {
3934     var r = this.getIntegerRGB();
3935     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3936     
3937   },
3938   toHSL : function()
3939   {
3940      var hsl = this.getHSL();
3941   // return the CSS HSL Color value
3942     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3943     
3944   },
3945   
3946   toHSV : function()
3947   {
3948     var rgb = this.toRGB();
3949     var hsv = rgb.getHSV();
3950    // return the CSS HSL Color value
3951     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3952     
3953   },
3954   
3955   // modify  v = 0 ... 1 (eg. 0.5)
3956   saturate : function(v)
3957   {
3958       var rgb = this.toRGB();
3959       var hsv = rgb.getHSV();
3960       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3961       
3962   
3963   },
3964   
3965    
3966   /**
3967    * getRGB
3968    * @return {Object} the RGB and alpha components of this Color as an object with r,
3969    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3970    * the range [0,1].
3971    */
3972   getRGB: function(){
3973    
3974     // return the RGB components
3975     return {
3976       'r' : this.rgb.r,
3977       'g' : this.rgb.g,
3978       'b' : this.rgb.b,
3979       'a' : this.alpha
3980     };
3981
3982   },
3983
3984   /**
3985    * getHSV
3986    * @return {Object} the HSV and alpha components of this Color as an object with h,
3987    * s, v, and a properties. h is in the range [0,360), s and v are in the range
3988    * [0,100], and a is in the range [0,1].
3989    */
3990   getHSV : function()
3991   {
3992     
3993     // calculate the HSV components if necessary
3994     if (this.hsv == null) {
3995       this.calculateHSV();
3996     }
3997
3998     // return the HSV components
3999     return {
4000       'h' : this.hsv.h,
4001       's' : this.hsv.s,
4002       'v' : this.hsv.v,
4003       'a' : this.alpha
4004     };
4005
4006   },
4007
4008   /**
4009    * getHSL
4010    * @return {Object} the HSL and alpha components of this Color as an object with h,
4011    * s, l, and a properties. h is in the range [0,360), s and l are in the range
4012    * [0,100], and a is in the range [0,1].
4013    */
4014   getHSL : function(){
4015     
4016      
4017     // calculate the HSV components if necessary
4018     if (this.hsl == null) { this.calculateHSL(); }
4019
4020     // return the HSL components
4021     return {
4022       'h' : this.hsl.h,
4023       's' : this.hsl.s,
4024       'l' : this.hsl.l,
4025       'a' : this.alpha
4026     };
4027
4028   }
4029   
4030
4031 });
4032
4033
4034 /**
4035  * @class Roo.lib.RGBColor
4036  * @extends Roo.lib.Color
4037  * Creates a Color specified in the RGB Color space, with an optional alpha
4038  * component. The parameters are:
4039  * @constructor
4040  * 
4041
4042  * @param {Number} r - the red component, clipped to the range [0,255]
4043  * @param {Number} g - the green component, clipped to the range [0,255]
4044  * @param {Number} b - the blue component, clipped to the range [0,255]
4045  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4046  *     optional and defaults to 1
4047  */
4048 Roo.lib.RGBColor = function (r, g, b, a){
4049
4050   // store the alpha component after clipping it if necessary
4051   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4052
4053   // store the RGB components after clipping them if necessary
4054   this.rgb =
4055       {
4056         'r' : Math.max(0, Math.min(255, r)),
4057         'g' : Math.max(0, Math.min(255, g)),
4058         'b' : Math.max(0, Math.min(255, b))
4059       };
4060
4061   // initialise the HSV and HSL components to null
4062   
4063
4064   /* 
4065    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4066    * range [0,360). The parameters are:
4067    *
4068    * maximum - the maximum of the RGB component values
4069    * range   - the range of the RGB component values
4070    */
4071    
4072
4073 }
4074 // this does an 'exteds'
4075 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4076
4077   
4078     getHue  : function(maximum, range)
4079     {
4080       var rgb = this.rgb;
4081        
4082       // check whether the range is zero
4083       if (range == 0){
4084   
4085         // set the hue to zero (any hue is acceptable as the Color is grey)
4086         var hue = 0;
4087   
4088       }else{
4089   
4090         // determine which of the components has the highest value and set the hue
4091         switch (maximum){
4092   
4093           // red has the highest value
4094           case rgb.r:
4095             var hue = (rgb.g - rgb.b) / range * 60;
4096             if (hue < 0) { hue += 360; }
4097             break;
4098   
4099           // green has the highest value
4100           case rgb.g:
4101             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4102             break;
4103   
4104           // blue has the highest value
4105           case rgb.b:
4106             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4107             break;
4108   
4109         }
4110   
4111       }
4112   
4113       // return the hue
4114       return hue;
4115   
4116     },
4117
4118   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4119    * be returned be the getHSV function.
4120    */
4121    calculateHSV : function(){
4122     var rgb = this.rgb;
4123     // get the maximum and range of the RGB component values
4124     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4125     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4126
4127     // store the HSV components
4128     this.hsv =
4129         {
4130           'h' : this.getHue(maximum, range),
4131           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4132           'v' : maximum / 2.55
4133         };
4134
4135   },
4136
4137   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4138    * be returned be the getHSL function.
4139    */
4140    calculateHSL : function(){
4141     var rgb = this.rgb;
4142     // get the maximum and range of the RGB component values
4143     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4144     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4145
4146     // determine the lightness in the range [0,1]
4147     var l = maximum / 255 - range / 510;
4148
4149     // store the HSL components
4150     this.hsl =
4151         {
4152           'h' : this.getHue(maximum, range),
4153           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4154           'l' : 100 * l
4155         };
4156
4157   }
4158
4159 });
4160
4161 /**
4162  * @class Roo.lib.HSVColor
4163  * @extends Roo.lib.Color
4164  * Creates a Color specified in the HSV Color space, with an optional alpha
4165  * component. The parameters are:
4166  * @constructor
4167  *
4168  * @param {Number} h - the hue component, wrapped to the range [0,360)
4169  * @param {Number} s - the saturation component, clipped to the range [0,100]
4170  * @param {Number} v - the value component, clipped to the range [0,100]
4171  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4172  *     optional and defaults to 1
4173  */
4174 Roo.lib.HSVColor = function (h, s, v, a){
4175
4176   // store the alpha component after clipping it if necessary
4177   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4178
4179   // store the HSV components after clipping or wrapping them if necessary
4180   this.hsv =
4181       {
4182         'h' : (h % 360 + 360) % 360,
4183         's' : Math.max(0, Math.min(100, s)),
4184         'v' : Math.max(0, Math.min(100, v))
4185       };
4186
4187   // initialise the RGB and HSL components to null
4188   this.rgb = null;
4189   this.hsl = null;
4190 }
4191
4192 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4193   /* Calculates and stores the RGB components of this HSVColor so that they can
4194    * be returned be the getRGB function.
4195    */
4196   calculateRGB: function ()
4197   {
4198     var hsv = this.hsv;
4199     // check whether the saturation is zero
4200     if (hsv.s == 0){
4201
4202       // set the Color to the appropriate shade of grey
4203       var r = hsv.v;
4204       var g = hsv.v;
4205       var b = hsv.v;
4206
4207     }else{
4208
4209       // set some temporary values
4210       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4211       var p  = hsv.v * (1 - hsv.s / 100);
4212       var q  = hsv.v * (1 - hsv.s / 100 * f);
4213       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4214
4215       // set the RGB Color components to their temporary values
4216       switch (Math.floor(hsv.h / 60)){
4217         case 0: var r = hsv.v; var g = t; var b = p; break;
4218         case 1: var r = q; var g = hsv.v; var b = p; break;
4219         case 2: var r = p; var g = hsv.v; var b = t; break;
4220         case 3: var r = p; var g = q; var b = hsv.v; break;
4221         case 4: var r = t; var g = p; var b = hsv.v; break;
4222         case 5: var r = hsv.v; var g = p; var b = q; break;
4223       }
4224
4225     }
4226
4227     // store the RGB components
4228     this.rgb =
4229         {
4230           'r' : r * 2.55,
4231           'g' : g * 2.55,
4232           'b' : b * 2.55
4233         };
4234
4235   },
4236
4237   /* Calculates and stores the HSL components of this HSVColor so that they can
4238    * be returned be the getHSL function.
4239    */
4240   calculateHSL : function (){
4241
4242     var hsv = this.hsv;
4243     // determine the lightness in the range [0,100]
4244     var l = (2 - hsv.s / 100) * hsv.v / 2;
4245
4246     // store the HSL components
4247     this.hsl =
4248         {
4249           'h' : hsv.h,
4250           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4251           'l' : l
4252         };
4253
4254     // correct a division-by-zero error
4255     if (isNaN(hsl.s)) { hsl.s = 0; }
4256
4257   } 
4258  
4259
4260 });
4261  
4262
4263 /**
4264  * @class Roo.lib.HSLColor
4265  * @extends Roo.lib.Color
4266  *
4267  * @constructor
4268  * Creates a Color specified in the HSL Color space, with an optional alpha
4269  * component. The parameters are:
4270  *
4271  * @param {Number} h - the hue component, wrapped to the range [0,360)
4272  * @param {Number} s - the saturation component, clipped to the range [0,100]
4273  * @param {Number} l - the lightness component, clipped to the range [0,100]
4274  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4275  *     optional and defaults to 1
4276  */
4277
4278 Roo.lib.HSLColor = function(h, s, l, a){
4279
4280   // store the alpha component after clipping it if necessary
4281   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4282
4283   // store the HSL components after clipping or wrapping them if necessary
4284   this.hsl =
4285       {
4286         'h' : (h % 360 + 360) % 360,
4287         's' : Math.max(0, Math.min(100, s)),
4288         'l' : Math.max(0, Math.min(100, l))
4289       };
4290
4291   // initialise the RGB and HSV components to null
4292 }
4293
4294 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4295
4296   /* Calculates and stores the RGB components of this HSLColor so that they can
4297    * be returned be the getRGB function.
4298    */
4299   calculateRGB: function (){
4300
4301     // check whether the saturation is zero
4302     if (this.hsl.s == 0){
4303
4304       // store the RGB components representing the appropriate shade of grey
4305       this.rgb =
4306           {
4307             'r' : this.hsl.l * 2.55,
4308             'g' : this.hsl.l * 2.55,
4309             'b' : this.hsl.l * 2.55
4310           };
4311
4312     }else{
4313
4314       // set some temporary values
4315       var p = this.hsl.l < 50
4316             ? this.hsl.l * (1 + hsl.s / 100)
4317             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4318       var q = 2 * hsl.l - p;
4319
4320       // initialise the RGB components
4321       this.rgb =
4322           {
4323             'r' : (h + 120) / 60 % 6,
4324             'g' : h / 60,
4325             'b' : (h + 240) / 60 % 6
4326           };
4327
4328       // loop over the RGB components
4329       for (var key in this.rgb){
4330
4331         // ensure that the property is not inherited from the root object
4332         if (this.rgb.hasOwnProperty(key)){
4333
4334           // set the component to its value in the range [0,100]
4335           if (this.rgb[key] < 1){
4336             this.rgb[key] = q + (p - q) * this.rgb[key];
4337           }else if (this.rgb[key] < 3){
4338             this.rgb[key] = p;
4339           }else if (this.rgb[key] < 4){
4340             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4341           }else{
4342             this.rgb[key] = q;
4343           }
4344
4345           // set the component to its value in the range [0,255]
4346           this.rgb[key] *= 2.55;
4347
4348         }
4349
4350       }
4351
4352     }
4353
4354   },
4355
4356   /* Calculates and stores the HSV components of this HSLColor so that they can
4357    * be returned be the getHSL function.
4358    */
4359    calculateHSV : function(){
4360
4361     // set a temporary value
4362     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4363
4364     // store the HSV components
4365     this.hsv =
4366         {
4367           'h' : this.hsl.h,
4368           's' : 200 * t / (this.hsl.l + t),
4369           'v' : t + this.hsl.l
4370         };
4371
4372     // correct a division-by-zero error
4373     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4374
4375   }
4376  
4377
4378 });
4379 /*
4380  * Portions of this file are based on pieces of Yahoo User Interface Library
4381  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4382  * YUI licensed under the BSD License:
4383  * http://developer.yahoo.net/yui/license.txt
4384  * <script type="text/javascript">
4385  *
4386  */
4387 (function() {
4388
4389     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4390         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4391     };
4392
4393     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4394
4395     var fly = Roo.lib.AnimBase.fly;
4396     var Y = Roo.lib;
4397     var superclass = Y.ColorAnim.superclass;
4398     var proto = Y.ColorAnim.prototype;
4399
4400     proto.toString = function() {
4401         var el = this.getEl();
4402         var id = el.id || el.tagName;
4403         return ("ColorAnim " + id);
4404     };
4405
4406     proto.patterns.color = /color$/i;
4407     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4408     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4409     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4410     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4411
4412
4413     proto.parseColor = function(s) {
4414         if (s.length == 3) {
4415             return s;
4416         }
4417
4418         var c = this.patterns.hex.exec(s);
4419         if (c && c.length == 4) {
4420             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4421         }
4422
4423         c = this.patterns.rgb.exec(s);
4424         if (c && c.length == 4) {
4425             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4426         }
4427
4428         c = this.patterns.hex3.exec(s);
4429         if (c && c.length == 4) {
4430             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4431         }
4432
4433         return null;
4434     };
4435     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4436     proto.getAttribute = function(attr) {
4437         var el = this.getEl();
4438         if (this.patterns.color.test(attr)) {
4439             var val = fly(el).getStyle(attr);
4440
4441             if (this.patterns.transparent.test(val)) {
4442                 var parent = el.parentNode;
4443                 val = fly(parent).getStyle(attr);
4444
4445                 while (parent && this.patterns.transparent.test(val)) {
4446                     parent = parent.parentNode;
4447                     val = fly(parent).getStyle(attr);
4448                     if (parent.tagName.toUpperCase() == 'HTML') {
4449                         val = '#fff';
4450                     }
4451                 }
4452             }
4453         } else {
4454             val = superclass.getAttribute.call(this, attr);
4455         }
4456
4457         return val;
4458     };
4459     proto.getAttribute = function(attr) {
4460         var el = this.getEl();
4461         if (this.patterns.color.test(attr)) {
4462             var val = fly(el).getStyle(attr);
4463
4464             if (this.patterns.transparent.test(val)) {
4465                 var parent = el.parentNode;
4466                 val = fly(parent).getStyle(attr);
4467
4468                 while (parent && this.patterns.transparent.test(val)) {
4469                     parent = parent.parentNode;
4470                     val = fly(parent).getStyle(attr);
4471                     if (parent.tagName.toUpperCase() == 'HTML') {
4472                         val = '#fff';
4473                     }
4474                 }
4475             }
4476         } else {
4477             val = superclass.getAttribute.call(this, attr);
4478         }
4479
4480         return val;
4481     };
4482
4483     proto.doMethod = function(attr, start, end) {
4484         var val;
4485
4486         if (this.patterns.color.test(attr)) {
4487             val = [];
4488             for (var i = 0, len = start.length; i < len; ++i) {
4489                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4490             }
4491
4492             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4493         }
4494         else {
4495             val = superclass.doMethod.call(this, attr, start, end);
4496         }
4497
4498         return val;
4499     };
4500
4501     proto.setRuntimeAttribute = function(attr) {
4502         superclass.setRuntimeAttribute.call(this, attr);
4503
4504         if (this.patterns.color.test(attr)) {
4505             var attributes = this.attributes;
4506             var start = this.parseColor(this.runtimeAttributes[attr].start);
4507             var end = this.parseColor(this.runtimeAttributes[attr].end);
4508
4509             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4510                 end = this.parseColor(attributes[attr].by);
4511
4512                 for (var i = 0, len = start.length; i < len; ++i) {
4513                     end[i] = start[i] + end[i];
4514                 }
4515             }
4516
4517             this.runtimeAttributes[attr].start = start;
4518             this.runtimeAttributes[attr].end = end;
4519         }
4520     };
4521 })();
4522
4523 /*
4524  * Portions of this file are based on pieces of Yahoo User Interface Library
4525  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4526  * YUI licensed under the BSD License:
4527  * http://developer.yahoo.net/yui/license.txt
4528  * <script type="text/javascript">
4529  *
4530  */
4531 Roo.lib.Easing = {
4532
4533
4534     easeNone: function (t, b, c, d) {
4535         return c * t / d + b;
4536     },
4537
4538
4539     easeIn: function (t, b, c, d) {
4540         return c * (t /= d) * t + b;
4541     },
4542
4543
4544     easeOut: function (t, b, c, d) {
4545         return -c * (t /= d) * (t - 2) + b;
4546     },
4547
4548
4549     easeBoth: function (t, b, c, d) {
4550         if ((t /= d / 2) < 1) {
4551             return c / 2 * t * t + b;
4552         }
4553
4554         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4555     },
4556
4557
4558     easeInStrong: function (t, b, c, d) {
4559         return c * (t /= d) * t * t * t + b;
4560     },
4561
4562
4563     easeOutStrong: function (t, b, c, d) {
4564         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4565     },
4566
4567
4568     easeBothStrong: function (t, b, c, d) {
4569         if ((t /= d / 2) < 1) {
4570             return c / 2 * t * t * t * t + b;
4571         }
4572
4573         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4574     },
4575
4576
4577
4578     elasticIn: function (t, b, c, d, a, p) {
4579         if (t == 0) {
4580             return b;
4581         }
4582         if ((t /= d) == 1) {
4583             return b + c;
4584         }
4585         if (!p) {
4586             p = d * .3;
4587         }
4588
4589         if (!a || a < Math.abs(c)) {
4590             a = c;
4591             var s = p / 4;
4592         }
4593         else {
4594             var s = p / (2 * Math.PI) * Math.asin(c / a);
4595         }
4596
4597         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4598     },
4599
4600
4601     elasticOut: function (t, b, c, d, a, p) {
4602         if (t == 0) {
4603             return b;
4604         }
4605         if ((t /= d) == 1) {
4606             return b + c;
4607         }
4608         if (!p) {
4609             p = d * .3;
4610         }
4611
4612         if (!a || a < Math.abs(c)) {
4613             a = c;
4614             var s = p / 4;
4615         }
4616         else {
4617             var s = p / (2 * Math.PI) * Math.asin(c / a);
4618         }
4619
4620         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4621     },
4622
4623
4624     elasticBoth: function (t, b, c, d, a, p) {
4625         if (t == 0) {
4626             return b;
4627         }
4628
4629         if ((t /= d / 2) == 2) {
4630             return b + c;
4631         }
4632
4633         if (!p) {
4634             p = d * (.3 * 1.5);
4635         }
4636
4637         if (!a || a < Math.abs(c)) {
4638             a = c;
4639             var s = p / 4;
4640         }
4641         else {
4642             var s = p / (2 * Math.PI) * Math.asin(c / a);
4643         }
4644
4645         if (t < 1) {
4646             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4647                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4648         }
4649         return a * Math.pow(2, -10 * (t -= 1)) *
4650                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4651     },
4652
4653
4654
4655     backIn: function (t, b, c, d, s) {
4656         if (typeof s == 'undefined') {
4657             s = 1.70158;
4658         }
4659         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4660     },
4661
4662
4663     backOut: function (t, b, c, d, s) {
4664         if (typeof s == 'undefined') {
4665             s = 1.70158;
4666         }
4667         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4668     },
4669
4670
4671     backBoth: function (t, b, c, d, s) {
4672         if (typeof s == 'undefined') {
4673             s = 1.70158;
4674         }
4675
4676         if ((t /= d / 2 ) < 1) {
4677             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4678         }
4679         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4680     },
4681
4682
4683     bounceIn: function (t, b, c, d) {
4684         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4685     },
4686
4687
4688     bounceOut: function (t, b, c, d) {
4689         if ((t /= d) < (1 / 2.75)) {
4690             return c * (7.5625 * t * t) + b;
4691         } else if (t < (2 / 2.75)) {
4692             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4693         } else if (t < (2.5 / 2.75)) {
4694             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4695         }
4696         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4697     },
4698
4699
4700     bounceBoth: function (t, b, c, d) {
4701         if (t < d / 2) {
4702             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4703         }
4704         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4705     }
4706 };/*
4707  * Portions of this file are based on pieces of Yahoo User Interface Library
4708  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4709  * YUI licensed under the BSD License:
4710  * http://developer.yahoo.net/yui/license.txt
4711  * <script type="text/javascript">
4712  *
4713  */
4714     (function() {
4715         Roo.lib.Motion = function(el, attributes, duration, method) {
4716             if (el) {
4717                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4718             }
4719         };
4720
4721         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4722
4723
4724         var Y = Roo.lib;
4725         var superclass = Y.Motion.superclass;
4726         var proto = Y.Motion.prototype;
4727
4728         proto.toString = function() {
4729             var el = this.getEl();
4730             var id = el.id || el.tagName;
4731             return ("Motion " + id);
4732         };
4733
4734         proto.patterns.points = /^points$/i;
4735
4736         proto.setAttribute = function(attr, val, unit) {
4737             if (this.patterns.points.test(attr)) {
4738                 unit = unit || 'px';
4739                 superclass.setAttribute.call(this, 'left', val[0], unit);
4740                 superclass.setAttribute.call(this, 'top', val[1], unit);
4741             } else {
4742                 superclass.setAttribute.call(this, attr, val, unit);
4743             }
4744         };
4745
4746         proto.getAttribute = function(attr) {
4747             if (this.patterns.points.test(attr)) {
4748                 var val = [
4749                         superclass.getAttribute.call(this, 'left'),
4750                         superclass.getAttribute.call(this, 'top')
4751                         ];
4752             } else {
4753                 val = superclass.getAttribute.call(this, attr);
4754             }
4755
4756             return val;
4757         };
4758
4759         proto.doMethod = function(attr, start, end) {
4760             var val = null;
4761
4762             if (this.patterns.points.test(attr)) {
4763                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4764                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4765             } else {
4766                 val = superclass.doMethod.call(this, attr, start, end);
4767             }
4768             return val;
4769         };
4770
4771         proto.setRuntimeAttribute = function(attr) {
4772             if (this.patterns.points.test(attr)) {
4773                 var el = this.getEl();
4774                 var attributes = this.attributes;
4775                 var start;
4776                 var control = attributes['points']['control'] || [];
4777                 var end;
4778                 var i, len;
4779
4780                 if (control.length > 0 && !(control[0] instanceof Array)) {
4781                     control = [control];
4782                 } else {
4783                     var tmp = [];
4784                     for (i = 0,len = control.length; i < len; ++i) {
4785                         tmp[i] = control[i];
4786                     }
4787                     control = tmp;
4788                 }
4789
4790                 Roo.fly(el).position();
4791
4792                 if (isset(attributes['points']['from'])) {
4793                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4794                 }
4795                 else {
4796                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4797                 }
4798
4799                 start = this.getAttribute('points');
4800
4801
4802                 if (isset(attributes['points']['to'])) {
4803                     end = translateValues.call(this, attributes['points']['to'], start);
4804
4805                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4806                     for (i = 0,len = control.length; i < len; ++i) {
4807                         control[i] = translateValues.call(this, control[i], start);
4808                     }
4809
4810
4811                 } else if (isset(attributes['points']['by'])) {
4812                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4813
4814                     for (i = 0,len = control.length; i < len; ++i) {
4815                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4816                     }
4817                 }
4818
4819                 this.runtimeAttributes[attr] = [start];
4820
4821                 if (control.length > 0) {
4822                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4823                 }
4824
4825                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4826             }
4827             else {
4828                 superclass.setRuntimeAttribute.call(this, attr);
4829             }
4830         };
4831
4832         var translateValues = function(val, start) {
4833             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4834             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4835
4836             return val;
4837         };
4838
4839         var isset = function(prop) {
4840             return (typeof prop !== 'undefined');
4841         };
4842     })();
4843 /*
4844  * Portions of this file are based on pieces of Yahoo User Interface Library
4845  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4846  * YUI licensed under the BSD License:
4847  * http://developer.yahoo.net/yui/license.txt
4848  * <script type="text/javascript">
4849  *
4850  */
4851     (function() {
4852         Roo.lib.Scroll = function(el, attributes, duration, method) {
4853             if (el) {
4854                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4855             }
4856         };
4857
4858         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4859
4860
4861         var Y = Roo.lib;
4862         var superclass = Y.Scroll.superclass;
4863         var proto = Y.Scroll.prototype;
4864
4865         proto.toString = function() {
4866             var el = this.getEl();
4867             var id = el.id || el.tagName;
4868             return ("Scroll " + id);
4869         };
4870
4871         proto.doMethod = function(attr, start, end) {
4872             var val = null;
4873
4874             if (attr == 'scroll') {
4875                 val = [
4876                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4877                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4878                         ];
4879
4880             } else {
4881                 val = superclass.doMethod.call(this, attr, start, end);
4882             }
4883             return val;
4884         };
4885
4886         proto.getAttribute = function(attr) {
4887             var val = null;
4888             var el = this.getEl();
4889
4890             if (attr == 'scroll') {
4891                 val = [ el.scrollLeft, el.scrollTop ];
4892             } else {
4893                 val = superclass.getAttribute.call(this, attr);
4894             }
4895
4896             return val;
4897         };
4898
4899         proto.setAttribute = function(attr, val, unit) {
4900             var el = this.getEl();
4901
4902             if (attr == 'scroll') {
4903                 el.scrollLeft = val[0];
4904                 el.scrollTop = val[1];
4905             } else {
4906                 superclass.setAttribute.call(this, attr, val, unit);
4907             }
4908         };
4909     })();
4910 /**
4911  * Originally based of this code... - refactored for Roo...
4912  * https://github.com/aaalsaleh/undo-manager
4913  
4914  * undo-manager.js
4915  * @author  Abdulrahman Alsaleh 
4916  * @copyright 2015 Abdulrahman Alsaleh 
4917  * @license  MIT License (c) 
4918  *
4919  * Hackily modifyed by alan@roojs.com
4920  *
4921  *
4922  *  
4923  *
4924  *  TOTALLY UNTESTED...
4925  *
4926  *  Documentation to be done....
4927  */
4928  
4929
4930 /**
4931 * @class Roo.lib.UndoManager
4932 * An undo manager implementation in JavaScript. It follows the W3C UndoManager and DOM Transaction
4933 * Draft and the undocumented and disabled Mozilla Firefox's UndoManager implementation.
4934
4935  * Usage:
4936  * <pre><code>
4937
4938
4939 editor.undoManager = new Roo.lib.UndoManager(1000, editor);
4940  
4941 </code></pre>
4942
4943 * For more information see this blog post with examples:
4944 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4945      - Create Elements using DOM, HTML fragments and Templates</a>. 
4946 * @constructor
4947 * @param {Number} limit how far back to go ... use 1000?
4948 * @param {Object} scope usually use document..
4949 */
4950
4951 Roo.lib.UndoManager = function (limit, undoScopeHost)
4952 {
4953     this.stack = [];
4954     this.limit = limit;
4955     this.scope = undoScopeHost;
4956     this.fireEvent = typeof CustomEvent != 'undefined' && undoScopeHost && undoScopeHost.dispatchEvent;
4957     if (this.fireEvent) {
4958         this.bindEvents();
4959     }
4960     this.reset();
4961     
4962 };
4963         
4964 Roo.lib.UndoManager.prototype = {
4965     
4966     limit : false,
4967     stack : false,
4968     scope :  false,
4969     fireEvent : false,
4970     position : 0,
4971     length : 0,
4972     
4973     
4974      /**
4975      * To push and execute a transaction, the method undoManager.transact
4976      * must be called by passing a transaction object as the first argument, and a merge
4977      * flag as the second argument. A transaction object has the following properties:
4978      *
4979      * Usage:
4980 <pre><code>
4981 undoManager.transact({
4982     label: 'Typing',
4983     execute: function() { ... },
4984     undo: function() { ... },
4985     // redo same as execute
4986     redo: function() { this.execute(); }
4987 }, false);
4988
4989 // merge transaction
4990 undoManager.transact({
4991     label: 'Typing',
4992     execute: function() { ... },  // this will be run...
4993     undo: function() { ... }, // what to do when undo is run.
4994     // redo same as execute
4995     redo: function() { this.execute(); }
4996 }, true); 
4997 </code></pre> 
4998      *
4999      * 
5000      * @param {Object} transaction The transaction to add to the stack.
5001      * @return {String} The HTML fragment
5002      */
5003     
5004     
5005     transact : function (transaction, merge)
5006     {
5007         if (arguments.length < 2) {
5008             throw new TypeError('Not enough arguments to UndoManager.transact.');
5009         }
5010
5011         transaction.execute();
5012
5013         this.stack.splice(0, this.position);
5014         if (merge && this.length) {
5015             this.stack[0].push(transaction);
5016         } else {
5017             this.stack.unshift([transaction]);
5018         }
5019     
5020         this.position = 0;
5021
5022         if (this.limit && this.stack.length > this.limit) {
5023             this.length = this.stack.length = this.limit;
5024         } else {
5025             this.length = this.stack.length;
5026         }
5027
5028         if (this.fireEvent) {
5029             this.scope.dispatchEvent(
5030                 new CustomEvent('DOMTransaction', {
5031                     detail: {
5032                         transactions: this.stack[0].slice()
5033                     },
5034                     bubbles: true,
5035                     cancelable: false
5036                 })
5037             );
5038         }
5039         
5040         //Roo.log("transaction: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5041       
5042         
5043     },
5044
5045     undo : function ()
5046     {
5047         //Roo.log("undo: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5048         
5049         if (this.position < this.length) {
5050             for (var i = this.stack[this.position].length - 1; i >= 0; i--) {
5051                 this.stack[this.position][i].undo();
5052             }
5053             this.position++;
5054
5055             if (this.fireEvent) {
5056                 this.scope.dispatchEvent(
5057                     new CustomEvent('undo', {
5058                         detail: {
5059                             transactions: this.stack[this.position - 1].slice()
5060                         },
5061                         bubbles: true,
5062                         cancelable: false
5063                     })
5064                 );
5065             }
5066         }
5067     },
5068
5069     redo : function ()
5070     {
5071         if (this.position > 0) {
5072             for (var i = 0, n = this.stack[this.position - 1].length; i < n; i++) {
5073                 this.stack[this.position - 1][i].redo();
5074             }
5075             this.position--;
5076
5077             if (this.fireEvent) {
5078                 this.scope.dispatchEvent(
5079                     new CustomEvent('redo', {
5080                         detail: {
5081                             transactions: this.stack[this.position].slice()
5082                         },
5083                         bubbles: true,
5084                         cancelable: false
5085                     })
5086                 );
5087             }
5088         }
5089     },
5090
5091     item : function (index)
5092     {
5093         if (index >= 0 && index < this.length) {
5094             return this.stack[index].slice();
5095         }
5096         return null;
5097     },
5098
5099     clearUndo : function () {
5100         this.stack.length = this.length = this.position;
5101     },
5102
5103     clearRedo : function () {
5104         this.stack.splice(0, this.position);
5105         this.position = 0;
5106         this.length = this.stack.length;
5107     },
5108     /**
5109      * Reset the undo - probaly done on load to clear all history.
5110      */
5111     reset : function()
5112     {
5113         this.stack = [];
5114         this.position = 0;
5115         this.length = 0;
5116         this.current_html = this.scope.innerHTML;
5117         if (this.timer !== false) {
5118             clearTimeout(this.timer);
5119         }
5120         this.timer = false;
5121         this.merge = false;
5122         this.addEvent();
5123         
5124     },
5125     current_html : '',
5126     timer : false,
5127     merge : false,
5128     
5129     
5130     // this will handle the undo/redo on the element.?
5131     bindEvents : function()
5132     {
5133         var el  = this.scope;
5134         el.undoManager = this;
5135         
5136         
5137         this.scope.addEventListener('keydown', function(e) {
5138             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5139                 if (e.shiftKey) {
5140                     el.undoManager.redo(); // Ctrl/Command + Shift + Z
5141                 } else {
5142                     el.undoManager.undo(); // Ctrl/Command + Z
5143                 }
5144         
5145                 e.preventDefault();
5146             }
5147         });
5148         /// ignore keyup..
5149         this.scope.addEventListener('keyup', function(e) {
5150             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5151                 e.preventDefault();
5152             }
5153         });
5154         
5155         
5156         
5157         var t = this;
5158         
5159         el.addEventListener('input', function(e) {
5160             if(el.innerHTML == t.current_html) {
5161                 return;
5162             }
5163             // only record events every second.
5164             if (t.timer !== false) {
5165                clearTimeout(t.timer);
5166                t.timer = false;
5167             }
5168             t.timer = setTimeout(function() { t.merge = false; }, 1000);
5169             
5170             t.addEvent(t.merge);
5171             t.merge = true; // ignore changes happening every second..
5172         });
5173         },
5174     /**
5175      * Manually add an event.
5176      * Normall called without arguements - and it will just get added to the stack.
5177      * 
5178      */
5179     
5180     addEvent : function(merge)
5181     {
5182         //Roo.log("undomanager +" + (merge ? 'Y':'n'));
5183         // not sure if this should clear the timer 
5184         merge = typeof(merge) == 'undefined' ? false : merge; 
5185         
5186         this.scope.undoManager.transact({
5187             scope : this.scope,
5188             oldHTML: this.current_html,
5189             newHTML: this.scope.innerHTML,
5190             // nothing to execute (content already changed when input is fired)
5191             execute: function() { },
5192             undo: function() {
5193                 this.scope.innerHTML = this.current_html = this.oldHTML;
5194             },
5195             redo: function() {
5196                 this.scope.innerHTML = this.current_html = this.newHTML;
5197             }
5198         }, false); //merge);
5199         
5200         this.merge = merge;
5201         
5202         this.current_html = this.scope.innerHTML;
5203     }
5204     
5205     
5206      
5207     
5208     
5209     
5210 };
5211 /*
5212  * Based on:
5213  * Ext JS Library 1.1.1
5214  * Copyright(c) 2006-2007, Ext JS, LLC.
5215  *
5216  * Originally Released Under LGPL - original licence link has changed is not relivant.
5217  *
5218  * Fork - LGPL
5219  * <script type="text/javascript">
5220  */
5221
5222
5223 // nasty IE9 hack - what a pile of crap that is..
5224
5225  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
5226     Range.prototype.createContextualFragment = function (html) {
5227         var doc = window.document;
5228         var container = doc.createElement("div");
5229         container.innerHTML = html;
5230         var frag = doc.createDocumentFragment(), n;
5231         while ((n = container.firstChild)) {
5232             frag.appendChild(n);
5233         }
5234         return frag;
5235     };
5236 }
5237
5238 /**
5239  * @class Roo.DomHelper
5240  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
5241  * 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>.
5242  * @static
5243  */
5244 Roo.DomHelper = function(){
5245     var tempTableEl = null;
5246     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
5247     var tableRe = /^table|tbody|tr|td$/i;
5248     var xmlns = {};
5249     // build as innerHTML where available
5250     /** @ignore */
5251     var createHtml = function(o){
5252         if(typeof o == 'string'){
5253             return o;
5254         }
5255         var b = "";
5256         if(!o.tag){
5257             o.tag = "div";
5258         }
5259         b += "<" + o.tag;
5260         for(var attr in o){
5261             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
5262             if(attr == "style"){
5263                 var s = o["style"];
5264                 if(typeof s == "function"){
5265                     s = s.call();
5266                 }
5267                 if(typeof s == "string"){
5268                     b += ' style="' + s + '"';
5269                 }else if(typeof s == "object"){
5270                     b += ' style="';
5271                     for(var key in s){
5272                         if(typeof s[key] != "function"){
5273                             b += key + ":" + s[key] + ";";
5274                         }
5275                     }
5276                     b += '"';
5277                 }
5278             }else{
5279                 if(attr == "cls"){
5280                     b += ' class="' + o["cls"] + '"';
5281                 }else if(attr == "htmlFor"){
5282                     b += ' for="' + o["htmlFor"] + '"';
5283                 }else{
5284                     b += " " + attr + '="' + o[attr] + '"';
5285                 }
5286             }
5287         }
5288         if(emptyTags.test(o.tag)){
5289             b += "/>";
5290         }else{
5291             b += ">";
5292             var cn = o.children || o.cn;
5293             if(cn){
5294                 //http://bugs.kde.org/show_bug.cgi?id=71506
5295                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5296                     for(var i = 0, len = cn.length; i < len; i++) {
5297                         b += createHtml(cn[i], b);
5298                     }
5299                 }else{
5300                     b += createHtml(cn, b);
5301                 }
5302             }
5303             if(o.html){
5304                 b += o.html;
5305             }
5306             b += "</" + o.tag + ">";
5307         }
5308         return b;
5309     };
5310
5311     // build as dom
5312     /** @ignore */
5313     var createDom = function(o, parentNode){
5314          
5315         // defininition craeted..
5316         var ns = false;
5317         if (o.ns && o.ns != 'html') {
5318                
5319             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5320                 xmlns[o.ns] = o.xmlns;
5321                 ns = o.xmlns;
5322             }
5323             if (typeof(xmlns[o.ns]) == 'undefined') {
5324                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5325             }
5326             ns = xmlns[o.ns];
5327         }
5328         
5329         
5330         if (typeof(o) == 'string') {
5331             return parentNode.appendChild(document.createTextNode(o));
5332         }
5333         o.tag = o.tag || div;
5334         if (o.ns && Roo.isIE) {
5335             ns = false;
5336             o.tag = o.ns + ':' + o.tag;
5337             
5338         }
5339         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5340         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5341         for(var attr in o){
5342             
5343             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5344                     attr == "style" || typeof o[attr] == "function") { continue; }
5345                     
5346             if(attr=="cls" && Roo.isIE){
5347                 el.className = o["cls"];
5348             }else{
5349                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5350                 else { 
5351                     el[attr] = o[attr];
5352                 }
5353             }
5354         }
5355         Roo.DomHelper.applyStyles(el, o.style);
5356         var cn = o.children || o.cn;
5357         if(cn){
5358             //http://bugs.kde.org/show_bug.cgi?id=71506
5359              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5360                 for(var i = 0, len = cn.length; i < len; i++) {
5361                     createDom(cn[i], el);
5362                 }
5363             }else{
5364                 createDom(cn, el);
5365             }
5366         }
5367         if(o.html){
5368             el.innerHTML = o.html;
5369         }
5370         if(parentNode){
5371            parentNode.appendChild(el);
5372         }
5373         return el;
5374     };
5375
5376     var ieTable = function(depth, s, h, e){
5377         tempTableEl.innerHTML = [s, h, e].join('');
5378         var i = -1, el = tempTableEl;
5379         while(++i < depth && el.firstChild){
5380             el = el.firstChild;
5381         }
5382         return el;
5383     };
5384
5385     // kill repeat to save bytes
5386     var ts = '<table>',
5387         te = '</table>',
5388         tbs = ts+'<tbody>',
5389         tbe = '</tbody>'+te,
5390         trs = tbs + '<tr>',
5391         tre = '</tr>'+tbe;
5392
5393     /**
5394      * @ignore
5395      * Nasty code for IE's broken table implementation
5396      */
5397     var insertIntoTable = function(tag, where, el, html){
5398         if(!tempTableEl){
5399             tempTableEl = document.createElement('div');
5400         }
5401         var node;
5402         var before = null;
5403         if(tag == 'td'){
5404             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5405                 return;
5406             }
5407             if(where == 'beforebegin'){
5408                 before = el;
5409                 el = el.parentNode;
5410             } else{
5411                 before = el.nextSibling;
5412                 el = el.parentNode;
5413             }
5414             node = ieTable(4, trs, html, tre);
5415         }
5416         else if(tag == 'tr'){
5417             if(where == 'beforebegin'){
5418                 before = el;
5419                 el = el.parentNode;
5420                 node = ieTable(3, tbs, html, tbe);
5421             } else if(where == 'afterend'){
5422                 before = el.nextSibling;
5423                 el = el.parentNode;
5424                 node = ieTable(3, tbs, html, tbe);
5425             } else{ // INTO a TR
5426                 if(where == 'afterbegin'){
5427                     before = el.firstChild;
5428                 }
5429                 node = ieTable(4, trs, html, tre);
5430             }
5431         } else if(tag == 'tbody'){
5432             if(where == 'beforebegin'){
5433                 before = el;
5434                 el = el.parentNode;
5435                 node = ieTable(2, ts, html, te);
5436             } else if(where == 'afterend'){
5437                 before = el.nextSibling;
5438                 el = el.parentNode;
5439                 node = ieTable(2, ts, html, te);
5440             } else{
5441                 if(where == 'afterbegin'){
5442                     before = el.firstChild;
5443                 }
5444                 node = ieTable(3, tbs, html, tbe);
5445             }
5446         } else{ // TABLE
5447             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5448                 return;
5449             }
5450             if(where == 'afterbegin'){
5451                 before = el.firstChild;
5452             }
5453             node = ieTable(2, ts, html, te);
5454         }
5455         el.insertBefore(node, before);
5456         return node;
5457     };
5458     
5459     // this is a bit like the react update code...
5460     // 
5461     
5462     var updateNode = function(from, to)
5463     {
5464         // should we handle non-standard elements?
5465         Roo.log(["UpdateNode" , from, to]);
5466         if (from.nodeType != to.nodeType) {
5467             Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5468             from.parentNode.replaceChild(to, from);
5469         }
5470         
5471         if (from.nodeType == 3) {
5472             // assume it's text?!
5473             if (from.data == to.data) {
5474                 return;
5475             }
5476             from.data = to.data;
5477             return;
5478         }
5479         
5480         // assume 'to' doesnt have '1/3 nodetypes!
5481         if (from.nodeType !=1 || from.tagName != to.tagName) {
5482             Roo.log(["ReplaceChild" , from, to ]);
5483             from.parentNode.replaceChild(to, from);
5484             return;
5485         }
5486         // compare attributes
5487         var ar = Array.from(from.attributes);
5488         for(var i = 0; i< ar.length;i++) {
5489             if (to.hasAttribute(ar[i].name)) {
5490                 continue;
5491             }
5492             if (ar[i].name == 'id') { // always keep ids?
5493                continue;
5494             }
5495             //if (ar[i].name == 'style') {
5496             //   throw "style removed?";
5497             //}
5498             Roo.log("removeAttribute" + ar[i].name);
5499             from.removeAttribute(ar[i].name);
5500         }
5501         ar = to.attributes;
5502         for(var i = 0; i< ar.length;i++) {
5503             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5504                 Roo.log("skipAttribute " + ar[i].name  + '=' + to.getAttribute(ar[i].name));
5505                 continue;
5506             }
5507             Roo.log("updateAttribute " + ar[i].name + '=>' + to.getAttribute(ar[i].name));
5508             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5509         }
5510         // children
5511         var far = Array.from(from.childNodes);
5512         var tar = Array.from(to.childNodes);
5513         // if the lengths are different.. then it's probably a editable content change, rather than
5514         // a change of the block definition..
5515         
5516         // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5517          /*if (from.innerHTML == to.innerHTML) {
5518             return;
5519         }
5520         if (far.length != tar.length) {
5521             from.innerHTML = to.innerHTML;
5522             return;
5523         }
5524         */
5525         
5526         for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5527             if (i >= far.length) {
5528                 from.appendChild(tar[i]);
5529                 Roo.log(["add", tar[i]]);
5530                 
5531             } else if ( i  >= tar.length) {
5532                 from.removeChild(far[i]);
5533                 Roo.log(["remove", far[i]]);
5534             } else {
5535                 
5536                 updateNode(far[i], tar[i]);
5537             }    
5538         }
5539         
5540         
5541         
5542         
5543     };
5544     
5545     
5546
5547     return {
5548         /** True to force the use of DOM instead of html fragments @type Boolean */
5549         useDom : false,
5550     
5551         /**
5552          * Returns the markup for the passed Element(s) config
5553          * @param {Object} o The Dom object spec (and children)
5554          * @return {String}
5555          */
5556         markup : function(o){
5557             return createHtml(o);
5558         },
5559     
5560         /**
5561          * Applies a style specification to an element
5562          * @param {String/HTMLElement} el The element to apply styles to
5563          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5564          * a function which returns such a specification.
5565          */
5566         applyStyles : function(el, styles){
5567             if(styles){
5568                el = Roo.fly(el);
5569                if(typeof styles == "string"){
5570                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5571                    var matches;
5572                    while ((matches = re.exec(styles)) != null){
5573                        el.setStyle(matches[1], matches[2]);
5574                    }
5575                }else if (typeof styles == "object"){
5576                    for (var style in styles){
5577                       el.setStyle(style, styles[style]);
5578                    }
5579                }else if (typeof styles == "function"){
5580                     Roo.DomHelper.applyStyles(el, styles.call());
5581                }
5582             }
5583         },
5584     
5585         /**
5586          * Inserts an HTML fragment into the Dom
5587          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5588          * @param {HTMLElement} el The context element
5589          * @param {String} html The HTML fragmenet
5590          * @return {HTMLElement} The new node
5591          */
5592         insertHtml : function(where, el, html){
5593             where = where.toLowerCase();
5594             if(el.insertAdjacentHTML){
5595                 if(tableRe.test(el.tagName)){
5596                     var rs;
5597                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5598                         return rs;
5599                     }
5600                 }
5601                 switch(where){
5602                     case "beforebegin":
5603                         el.insertAdjacentHTML('BeforeBegin', html);
5604                         return el.previousSibling;
5605                     case "afterbegin":
5606                         el.insertAdjacentHTML('AfterBegin', html);
5607                         return el.firstChild;
5608                     case "beforeend":
5609                         el.insertAdjacentHTML('BeforeEnd', html);
5610                         return el.lastChild;
5611                     case "afterend":
5612                         el.insertAdjacentHTML('AfterEnd', html);
5613                         return el.nextSibling;
5614                 }
5615                 throw 'Illegal insertion point -> "' + where + '"';
5616             }
5617             var range = el.ownerDocument.createRange();
5618             var frag;
5619             switch(where){
5620                  case "beforebegin":
5621                     range.setStartBefore(el);
5622                     frag = range.createContextualFragment(html);
5623                     el.parentNode.insertBefore(frag, el);
5624                     return el.previousSibling;
5625                  case "afterbegin":
5626                     if(el.firstChild){
5627                         range.setStartBefore(el.firstChild);
5628                         frag = range.createContextualFragment(html);
5629                         el.insertBefore(frag, el.firstChild);
5630                         return el.firstChild;
5631                     }else{
5632                         el.innerHTML = html;
5633                         return el.firstChild;
5634                     }
5635                 case "beforeend":
5636                     if(el.lastChild){
5637                         range.setStartAfter(el.lastChild);
5638                         frag = range.createContextualFragment(html);
5639                         el.appendChild(frag);
5640                         return el.lastChild;
5641                     }else{
5642                         el.innerHTML = html;
5643                         return el.lastChild;
5644                     }
5645                 case "afterend":
5646                     range.setStartAfter(el);
5647                     frag = range.createContextualFragment(html);
5648                     el.parentNode.insertBefore(frag, el.nextSibling);
5649                     return el.nextSibling;
5650                 }
5651                 throw 'Illegal insertion point -> "' + where + '"';
5652         },
5653     
5654         /**
5655          * Creates new Dom element(s) and inserts them before el
5656          * @param {String/HTMLElement/Element} el The context element
5657          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5658          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5659          * @return {HTMLElement/Roo.Element} The new node
5660          */
5661         insertBefore : function(el, o, returnElement){
5662             return this.doInsert(el, o, returnElement, "beforeBegin");
5663         },
5664     
5665         /**
5666          * Creates new Dom element(s) and inserts them after el
5667          * @param {String/HTMLElement/Element} el The context element
5668          * @param {Object} o The Dom object spec (and children)
5669          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5670          * @return {HTMLElement/Roo.Element} The new node
5671          */
5672         insertAfter : function(el, o, returnElement){
5673             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5674         },
5675     
5676         /**
5677          * Creates new Dom element(s) and inserts them as the first child of el
5678          * @param {String/HTMLElement/Element} el The context element
5679          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5680          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5681          * @return {HTMLElement/Roo.Element} The new node
5682          */
5683         insertFirst : function(el, o, returnElement){
5684             return this.doInsert(el, o, returnElement, "afterBegin");
5685         },
5686     
5687         // private
5688         doInsert : function(el, o, returnElement, pos, sibling){
5689             el = Roo.getDom(el);
5690             var newNode;
5691             if(this.useDom || o.ns){
5692                 newNode = createDom(o, null);
5693                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5694             }else{
5695                 var html = createHtml(o);
5696                 newNode = this.insertHtml(pos, el, html);
5697             }
5698             return returnElement ? Roo.get(newNode, true) : newNode;
5699         },
5700     
5701         /**
5702          * Creates new Dom element(s) and appends them to el
5703          * @param {String/HTMLElement/Element} el The context element
5704          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5705          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5706          * @return {HTMLElement/Roo.Element} The new node
5707          */
5708         append : function(el, o, returnElement){
5709             el = Roo.getDom(el);
5710             var newNode;
5711             if(this.useDom || o.ns){
5712                 newNode = createDom(o, null);
5713                 el.appendChild(newNode);
5714             }else{
5715                 var html = createHtml(o);
5716                 newNode = this.insertHtml("beforeEnd", el, html);
5717             }
5718             return returnElement ? Roo.get(newNode, true) : newNode;
5719         },
5720     
5721         /**
5722          * Creates new Dom element(s) and overwrites the contents of el with them
5723          * @param {String/HTMLElement/Element} el The context element
5724          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5725          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5726          * @return {HTMLElement/Roo.Element} The new node
5727          */
5728         overwrite : function(el, o, returnElement)
5729         {
5730             el = Roo.getDom(el);
5731             if (o.ns) {
5732               
5733                 while (el.childNodes.length) {
5734                     el.removeChild(el.firstChild);
5735                 }
5736                 createDom(o, el);
5737             } else {
5738                 el.innerHTML = createHtml(o);   
5739             }
5740             
5741             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5742         },
5743     
5744         /**
5745          * Creates a new Roo.DomHelper.Template from the Dom object spec
5746          * @param {Object} o The Dom object spec (and children)
5747          * @return {Roo.DomHelper.Template} The new template
5748          */
5749         createTemplate : function(o){
5750             var html = createHtml(o);
5751             return new Roo.Template(html);
5752         },
5753          /**
5754          * Updates the first element with the spec from the o (replacing if necessary)
5755          * This iterates through the children, and updates attributes / children etc..
5756          * @param {String/HTMLElement/Element} el The context element
5757          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5758          */
5759         
5760         update : function(el, o)
5761         {
5762             updateNode(Roo.getDom(el), createDom(o));
5763             
5764         }
5765         
5766         
5767     };
5768 }();
5769 /*
5770  * Based on:
5771  * Ext JS Library 1.1.1
5772  * Copyright(c) 2006-2007, Ext JS, LLC.
5773  *
5774  * Originally Released Under LGPL - original licence link has changed is not relivant.
5775  *
5776  * Fork - LGPL
5777  * <script type="text/javascript">
5778  */
5779  
5780 /**
5781 * @class Roo.Template
5782 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5783 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5784 * Usage:
5785 <pre><code>
5786 var t = new Roo.Template({
5787     html :  '&lt;div name="{id}"&gt;' + 
5788         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5789         '&lt;/div&gt;',
5790     myformat: function (value, allValues) {
5791         return 'XX' + value;
5792     }
5793 });
5794 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5795 </code></pre>
5796 * For more information see this blog post with examples:
5797 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5798      - Create Elements using DOM, HTML fragments and Templates</a>. 
5799 * @constructor
5800 * @param {Object} cfg - Configuration object.
5801 */
5802 Roo.Template = function(cfg){
5803     // BC!
5804     if(cfg instanceof Array){
5805         cfg = cfg.join("");
5806     }else if(arguments.length > 1){
5807         cfg = Array.prototype.join.call(arguments, "");
5808     }
5809     
5810     
5811     if (typeof(cfg) == 'object') {
5812         Roo.apply(this,cfg)
5813     } else {
5814         // bc
5815         this.html = cfg;
5816     }
5817     if (this.url) {
5818         this.load();
5819     }
5820     
5821 };
5822 Roo.Template.prototype = {
5823     
5824     /**
5825      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5826      */
5827     onLoad : false,
5828     
5829     
5830     /**
5831      * @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..
5832      *                    it should be fixed so that template is observable...
5833      */
5834     url : false,
5835     /**
5836      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5837      */
5838     html : '',
5839     
5840     
5841     compiled : false,
5842     loaded : false,
5843     /**
5844      * Returns an HTML fragment of this template with the specified values applied.
5845      * @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'})
5846      * @return {String} The HTML fragment
5847      */
5848     
5849    
5850     
5851     applyTemplate : function(values){
5852         //Roo.log(["applyTemplate", values]);
5853         try {
5854            
5855             if(this.compiled){
5856                 return this.compiled(values);
5857             }
5858             var useF = this.disableFormats !== true;
5859             var fm = Roo.util.Format, tpl = this;
5860             var fn = function(m, name, format, args){
5861                 if(format && useF){
5862                     if(format.substr(0, 5) == "this."){
5863                         return tpl.call(format.substr(5), values[name], values);
5864                     }else{
5865                         if(args){
5866                             // quoted values are required for strings in compiled templates, 
5867                             // but for non compiled we need to strip them
5868                             // quoted reversed for jsmin
5869                             var re = /^\s*['"](.*)["']\s*$/;
5870                             args = args.split(',');
5871                             for(var i = 0, len = args.length; i < len; i++){
5872                                 args[i] = args[i].replace(re, "$1");
5873                             }
5874                             args = [values[name]].concat(args);
5875                         }else{
5876                             args = [values[name]];
5877                         }
5878                         return fm[format].apply(fm, args);
5879                     }
5880                 }else{
5881                     return values[name] !== undefined ? values[name] : "";
5882                 }
5883             };
5884             return this.html.replace(this.re, fn);
5885         } catch (e) {
5886             Roo.log(e);
5887             throw e;
5888         }
5889          
5890     },
5891     
5892     loading : false,
5893       
5894     load : function ()
5895     {
5896          
5897         if (this.loading) {
5898             return;
5899         }
5900         var _t = this;
5901         
5902         this.loading = true;
5903         this.compiled = false;
5904         
5905         var cx = new Roo.data.Connection();
5906         cx.request({
5907             url : this.url,
5908             method : 'GET',
5909             success : function (response) {
5910                 _t.loading = false;
5911                 _t.url = false;
5912                 
5913                 _t.set(response.responseText,true);
5914                 _t.loaded = true;
5915                 if (_t.onLoad) {
5916                     _t.onLoad();
5917                 }
5918              },
5919             failure : function(response) {
5920                 Roo.log("Template failed to load from " + _t.url);
5921                 _t.loading = false;
5922             }
5923         });
5924     },
5925
5926     /**
5927      * Sets the HTML used as the template and optionally compiles it.
5928      * @param {String} html
5929      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5930      * @return {Roo.Template} this
5931      */
5932     set : function(html, compile){
5933         this.html = html;
5934         this.compiled = false;
5935         if(compile){
5936             this.compile();
5937         }
5938         return this;
5939     },
5940     
5941     /**
5942      * True to disable format functions (defaults to false)
5943      * @type Boolean
5944      */
5945     disableFormats : false,
5946     
5947     /**
5948     * The regular expression used to match template variables 
5949     * @type RegExp
5950     * @property 
5951     */
5952     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5953     
5954     /**
5955      * Compiles the template into an internal function, eliminating the RegEx overhead.
5956      * @return {Roo.Template} this
5957      */
5958     compile : function(){
5959         var fm = Roo.util.Format;
5960         var useF = this.disableFormats !== true;
5961         var sep = Roo.isGecko ? "+" : ",";
5962         var fn = function(m, name, format, args){
5963             if(format && useF){
5964                 args = args ? ',' + args : "";
5965                 if(format.substr(0, 5) != "this."){
5966                     format = "fm." + format + '(';
5967                 }else{
5968                     format = 'this.call("'+ format.substr(5) + '", ';
5969                     args = ", values";
5970                 }
5971             }else{
5972                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5973             }
5974             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5975         };
5976         var body;
5977         // branched to use + in gecko and [].join() in others
5978         if(Roo.isGecko){
5979             body = "this.compiled = function(values){ return '" +
5980                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5981                     "';};";
5982         }else{
5983             body = ["this.compiled = function(values){ return ['"];
5984             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5985             body.push("'].join('');};");
5986             body = body.join('');
5987         }
5988         /**
5989          * eval:var:values
5990          * eval:var:fm
5991          */
5992         eval(body);
5993         return this;
5994     },
5995     
5996     // private function used to call members
5997     call : function(fnName, value, allValues){
5998         return this[fnName](value, allValues);
5999     },
6000     
6001     /**
6002      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
6003      * @param {String/HTMLElement/Roo.Element} el The context element
6004      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6005      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6006      * @return {HTMLElement/Roo.Element} The new node or Element
6007      */
6008     insertFirst: function(el, values, returnElement){
6009         return this.doInsert('afterBegin', el, values, returnElement);
6010     },
6011
6012     /**
6013      * Applies the supplied values to the template and inserts the new node(s) before el.
6014      * @param {String/HTMLElement/Roo.Element} el The context element
6015      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6016      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6017      * @return {HTMLElement/Roo.Element} The new node or Element
6018      */
6019     insertBefore: function(el, values, returnElement){
6020         return this.doInsert('beforeBegin', el, values, returnElement);
6021     },
6022
6023     /**
6024      * Applies the supplied values to the template and inserts the new node(s) after el.
6025      * @param {String/HTMLElement/Roo.Element} el The context element
6026      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6027      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6028      * @return {HTMLElement/Roo.Element} The new node or Element
6029      */
6030     insertAfter : function(el, values, returnElement){
6031         return this.doInsert('afterEnd', el, values, returnElement);
6032     },
6033     
6034     /**
6035      * Applies the supplied values to the template and appends the new node(s) to el.
6036      * @param {String/HTMLElement/Roo.Element} el The context element
6037      * @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'})
6038      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6039      * @return {HTMLElement/Roo.Element} The new node or Element
6040      */
6041     append : function(el, values, returnElement){
6042         return this.doInsert('beforeEnd', el, values, returnElement);
6043     },
6044
6045     doInsert : function(where, el, values, returnEl){
6046         el = Roo.getDom(el);
6047         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
6048         return returnEl ? Roo.get(newNode, true) : newNode;
6049     },
6050
6051     /**
6052      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
6053      * @param {String/HTMLElement/Roo.Element} el The context element
6054      * @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'})
6055      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6056      * @return {HTMLElement/Roo.Element} The new node or Element
6057      */
6058     overwrite : function(el, values, returnElement){
6059         el = Roo.getDom(el);
6060         el.innerHTML = this.applyTemplate(values);
6061         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
6062     }
6063 };
6064 /**
6065  * Alias for {@link #applyTemplate}
6066  * @method
6067  */
6068 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
6069
6070 // backwards compat
6071 Roo.DomHelper.Template = Roo.Template;
6072
6073 /**
6074  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6075  * @param {String/HTMLElement} el A DOM element or its id
6076  * @returns {Roo.Template} The created template
6077  * @static
6078  */
6079 Roo.Template.from = function(el){
6080     el = Roo.getDom(el);
6081     return new Roo.Template(el.value || el.innerHTML);
6082 };/*
6083  * Based on:
6084  * Ext JS Library 1.1.1
6085  * Copyright(c) 2006-2007, Ext JS, LLC.
6086  *
6087  * Originally Released Under LGPL - original licence link has changed is not relivant.
6088  *
6089  * Fork - LGPL
6090  * <script type="text/javascript">
6091  */
6092  
6093
6094 /*
6095  * This is code is also distributed under MIT license for use
6096  * with jQuery and prototype JavaScript libraries.
6097  */
6098 /**
6099  * @class Roo.DomQuery
6100 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).
6101 <p>
6102 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>
6103
6104 <p>
6105 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.
6106 </p>
6107 <h4>Element Selectors:</h4>
6108 <ul class="list">
6109     <li> <b>*</b> any element</li>
6110     <li> <b>E</b> an element with the tag E</li>
6111     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
6112     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
6113     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
6114     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
6115 </ul>
6116 <h4>Attribute Selectors:</h4>
6117 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
6118 <ul class="list">
6119     <li> <b>E[foo]</b> has an attribute "foo"</li>
6120     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
6121     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
6122     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
6123     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
6124     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
6125     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
6126 </ul>
6127 <h4>Pseudo Classes:</h4>
6128 <ul class="list">
6129     <li> <b>E:first-child</b> E is the first child of its parent</li>
6130     <li> <b>E:last-child</b> E is the last child of its parent</li>
6131     <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>
6132     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
6133     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
6134     <li> <b>E:only-child</b> E is the only child of its parent</li>
6135     <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>
6136     <li> <b>E:first</b> the first E in the resultset</li>
6137     <li> <b>E:last</b> the last E in the resultset</li>
6138     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
6139     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
6140     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
6141     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
6142     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
6143     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
6144     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
6145     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
6146     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
6147 </ul>
6148 <h4>CSS Value Selectors:</h4>
6149 <ul class="list">
6150     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
6151     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
6152     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
6153     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
6154     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
6155     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
6156 </ul>
6157  * @static
6158  */
6159 Roo.DomQuery = function(){
6160     var cache = {}, simpleCache = {}, valueCache = {};
6161     var nonSpace = /\S/;
6162     var trimRe = /^\s+|\s+$/g;
6163     var tplRe = /\{(\d+)\}/g;
6164     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
6165     var tagTokenRe = /^(#)?([\w-\*]+)/;
6166     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
6167
6168     function child(p, index){
6169         var i = 0;
6170         var n = p.firstChild;
6171         while(n){
6172             if(n.nodeType == 1){
6173                if(++i == index){
6174                    return n;
6175                }
6176             }
6177             n = n.nextSibling;
6178         }
6179         return null;
6180     };
6181
6182     function next(n){
6183         while((n = n.nextSibling) && n.nodeType != 1);
6184         return n;
6185     };
6186
6187     function prev(n){
6188         while((n = n.previousSibling) && n.nodeType != 1);
6189         return n;
6190     };
6191
6192     function children(d){
6193         var n = d.firstChild, ni = -1;
6194             while(n){
6195                 var nx = n.nextSibling;
6196                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
6197                     d.removeChild(n);
6198                 }else{
6199                     n.nodeIndex = ++ni;
6200                 }
6201                 n = nx;
6202             }
6203             return this;
6204         };
6205
6206     function byClassName(c, a, v){
6207         if(!v){
6208             return c;
6209         }
6210         var r = [], ri = -1, cn;
6211         for(var i = 0, ci; ci = c[i]; i++){
6212             
6213             
6214             if((' '+
6215                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
6216                  +' ').indexOf(v) != -1){
6217                 r[++ri] = ci;
6218             }
6219         }
6220         return r;
6221     };
6222
6223     function attrValue(n, attr){
6224         if(!n.tagName && typeof n.length != "undefined"){
6225             n = n[0];
6226         }
6227         if(!n){
6228             return null;
6229         }
6230         if(attr == "for"){
6231             return n.htmlFor;
6232         }
6233         if(attr == "class" || attr == "className"){
6234             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
6235         }
6236         return n.getAttribute(attr) || n[attr];
6237
6238     };
6239
6240     function getNodes(ns, mode, tagName){
6241         var result = [], ri = -1, cs;
6242         if(!ns){
6243             return result;
6244         }
6245         tagName = tagName || "*";
6246         if(typeof ns.getElementsByTagName != "undefined"){
6247             ns = [ns];
6248         }
6249         if(!mode){
6250             for(var i = 0, ni; ni = ns[i]; i++){
6251                 cs = ni.getElementsByTagName(tagName);
6252                 for(var j = 0, ci; ci = cs[j]; j++){
6253                     result[++ri] = ci;
6254                 }
6255             }
6256         }else if(mode == "/" || mode == ">"){
6257             var utag = tagName.toUpperCase();
6258             for(var i = 0, ni, cn; ni = ns[i]; i++){
6259                 cn = ni.children || ni.childNodes;
6260                 for(var j = 0, cj; cj = cn[j]; j++){
6261                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
6262                         result[++ri] = cj;
6263                     }
6264                 }
6265             }
6266         }else if(mode == "+"){
6267             var utag = tagName.toUpperCase();
6268             for(var i = 0, n; n = ns[i]; i++){
6269                 while((n = n.nextSibling) && n.nodeType != 1);
6270                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
6271                     result[++ri] = n;
6272                 }
6273             }
6274         }else if(mode == "~"){
6275             for(var i = 0, n; n = ns[i]; i++){
6276                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
6277                 if(n){
6278                     result[++ri] = n;
6279                 }
6280             }
6281         }
6282         return result;
6283     };
6284
6285     function concat(a, b){
6286         if(b.slice){
6287             return a.concat(b);
6288         }
6289         for(var i = 0, l = b.length; i < l; i++){
6290             a[a.length] = b[i];
6291         }
6292         return a;
6293     }
6294
6295     function byTag(cs, tagName){
6296         if(cs.tagName || cs == document){
6297             cs = [cs];
6298         }
6299         if(!tagName){
6300             return cs;
6301         }
6302         var r = [], ri = -1;
6303         tagName = tagName.toLowerCase();
6304         for(var i = 0, ci; ci = cs[i]; i++){
6305             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
6306                 r[++ri] = ci;
6307             }
6308         }
6309         return r;
6310     };
6311
6312     function byId(cs, attr, id){
6313         if(cs.tagName || cs == document){
6314             cs = [cs];
6315         }
6316         if(!id){
6317             return cs;
6318         }
6319         var r = [], ri = -1;
6320         for(var i = 0,ci; ci = cs[i]; i++){
6321             if(ci && ci.id == id){
6322                 r[++ri] = ci;
6323                 return r;
6324             }
6325         }
6326         return r;
6327     };
6328
6329     function byAttribute(cs, attr, value, op, custom){
6330         var r = [], ri = -1, st = custom=="{";
6331         var f = Roo.DomQuery.operators[op];
6332         for(var i = 0, ci; ci = cs[i]; i++){
6333             var a;
6334             if(st){
6335                 a = Roo.DomQuery.getStyle(ci, attr);
6336             }
6337             else if(attr == "class" || attr == "className"){
6338                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6339             }else if(attr == "for"){
6340                 a = ci.htmlFor;
6341             }else if(attr == "href"){
6342                 a = ci.getAttribute("href", 2);
6343             }else{
6344                 a = ci.getAttribute(attr);
6345             }
6346             if((f && f(a, value)) || (!f && a)){
6347                 r[++ri] = ci;
6348             }
6349         }
6350         return r;
6351     };
6352
6353     function byPseudo(cs, name, value){
6354         return Roo.DomQuery.pseudos[name](cs, value);
6355     };
6356
6357     // This is for IE MSXML which does not support expandos.
6358     // IE runs the same speed using setAttribute, however FF slows way down
6359     // and Safari completely fails so they need to continue to use expandos.
6360     var isIE = window.ActiveXObject ? true : false;
6361
6362     // this eval is stop the compressor from
6363     // renaming the variable to something shorter
6364     
6365     /** eval:var:batch */
6366     var batch = 30803; 
6367
6368     var key = 30803;
6369
6370     function nodupIEXml(cs){
6371         var d = ++key;
6372         cs[0].setAttribute("_nodup", d);
6373         var r = [cs[0]];
6374         for(var i = 1, len = cs.length; i < len; i++){
6375             var c = cs[i];
6376             if(!c.getAttribute("_nodup") != d){
6377                 c.setAttribute("_nodup", d);
6378                 r[r.length] = c;
6379             }
6380         }
6381         for(var i = 0, len = cs.length; i < len; i++){
6382             cs[i].removeAttribute("_nodup");
6383         }
6384         return r;
6385     }
6386
6387     function nodup(cs){
6388         if(!cs){
6389             return [];
6390         }
6391         var len = cs.length, c, i, r = cs, cj, ri = -1;
6392         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6393             return cs;
6394         }
6395         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6396             return nodupIEXml(cs);
6397         }
6398         var d = ++key;
6399         cs[0]._nodup = d;
6400         for(i = 1; c = cs[i]; i++){
6401             if(c._nodup != d){
6402                 c._nodup = d;
6403             }else{
6404                 r = [];
6405                 for(var j = 0; j < i; j++){
6406                     r[++ri] = cs[j];
6407                 }
6408                 for(j = i+1; cj = cs[j]; j++){
6409                     if(cj._nodup != d){
6410                         cj._nodup = d;
6411                         r[++ri] = cj;
6412                     }
6413                 }
6414                 return r;
6415             }
6416         }
6417         return r;
6418     }
6419
6420     function quickDiffIEXml(c1, c2){
6421         var d = ++key;
6422         for(var i = 0, len = c1.length; i < len; i++){
6423             c1[i].setAttribute("_qdiff", d);
6424         }
6425         var r = [];
6426         for(var i = 0, len = c2.length; i < len; i++){
6427             if(c2[i].getAttribute("_qdiff") != d){
6428                 r[r.length] = c2[i];
6429             }
6430         }
6431         for(var i = 0, len = c1.length; i < len; i++){
6432            c1[i].removeAttribute("_qdiff");
6433         }
6434         return r;
6435     }
6436
6437     function quickDiff(c1, c2){
6438         var len1 = c1.length;
6439         if(!len1){
6440             return c2;
6441         }
6442         if(isIE && c1[0].selectSingleNode){
6443             return quickDiffIEXml(c1, c2);
6444         }
6445         var d = ++key;
6446         for(var i = 0; i < len1; i++){
6447             c1[i]._qdiff = d;
6448         }
6449         var r = [];
6450         for(var i = 0, len = c2.length; i < len; i++){
6451             if(c2[i]._qdiff != d){
6452                 r[r.length] = c2[i];
6453             }
6454         }
6455         return r;
6456     }
6457
6458     function quickId(ns, mode, root, id){
6459         if(ns == root){
6460            var d = root.ownerDocument || root;
6461            return d.getElementById(id);
6462         }
6463         ns = getNodes(ns, mode, "*");
6464         return byId(ns, null, id);
6465     }
6466
6467     return {
6468         getStyle : function(el, name){
6469             return Roo.fly(el).getStyle(name);
6470         },
6471         /**
6472          * Compiles a selector/xpath query into a reusable function. The returned function
6473          * takes one parameter "root" (optional), which is the context node from where the query should start.
6474          * @param {String} selector The selector/xpath query
6475          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6476          * @return {Function}
6477          */
6478         compile : function(path, type){
6479             type = type || "select";
6480             
6481             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6482             var q = path, mode, lq;
6483             var tk = Roo.DomQuery.matchers;
6484             var tklen = tk.length;
6485             var mm;
6486
6487             // accept leading mode switch
6488             var lmode = q.match(modeRe);
6489             if(lmode && lmode[1]){
6490                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6491                 q = q.replace(lmode[1], "");
6492             }
6493             // strip leading slashes
6494             while(path.substr(0, 1)=="/"){
6495                 path = path.substr(1);
6496             }
6497
6498             while(q && lq != q){
6499                 lq = q;
6500                 var tm = q.match(tagTokenRe);
6501                 if(type == "select"){
6502                     if(tm){
6503                         if(tm[1] == "#"){
6504                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6505                         }else{
6506                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6507                         }
6508                         q = q.replace(tm[0], "");
6509                     }else if(q.substr(0, 1) != '@'){
6510                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6511                     }
6512                 }else{
6513                     if(tm){
6514                         if(tm[1] == "#"){
6515                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6516                         }else{
6517                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6518                         }
6519                         q = q.replace(tm[0], "");
6520                     }
6521                 }
6522                 while(!(mm = q.match(modeRe))){
6523                     var matched = false;
6524                     for(var j = 0; j < tklen; j++){
6525                         var t = tk[j];
6526                         var m = q.match(t.re);
6527                         if(m){
6528                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6529                                                     return m[i];
6530                                                 });
6531                             q = q.replace(m[0], "");
6532                             matched = true;
6533                             break;
6534                         }
6535                     }
6536                     // prevent infinite loop on bad selector
6537                     if(!matched){
6538                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6539                     }
6540                 }
6541                 if(mm[1]){
6542                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6543                     q = q.replace(mm[1], "");
6544                 }
6545             }
6546             fn[fn.length] = "return nodup(n);\n}";
6547             
6548              /** 
6549               * list of variables that need from compression as they are used by eval.
6550              *  eval:var:batch 
6551              *  eval:var:nodup
6552              *  eval:var:byTag
6553              *  eval:var:ById
6554              *  eval:var:getNodes
6555              *  eval:var:quickId
6556              *  eval:var:mode
6557              *  eval:var:root
6558              *  eval:var:n
6559              *  eval:var:byClassName
6560              *  eval:var:byPseudo
6561              *  eval:var:byAttribute
6562              *  eval:var:attrValue
6563              * 
6564              **/ 
6565             eval(fn.join(""));
6566             return f;
6567         },
6568
6569         /**
6570          * Selects a group of elements.
6571          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6572          * @param {Node} root (optional) The start of the query (defaults to document).
6573          * @return {Array}
6574          */
6575         select : function(path, root, type){
6576             if(!root || root == document){
6577                 root = document;
6578             }
6579             if(typeof root == "string"){
6580                 root = document.getElementById(root);
6581             }
6582             var paths = path.split(",");
6583             var results = [];
6584             for(var i = 0, len = paths.length; i < len; i++){
6585                 var p = paths[i].replace(trimRe, "");
6586                 if(!cache[p]){
6587                     cache[p] = Roo.DomQuery.compile(p);
6588                     if(!cache[p]){
6589                         throw p + " is not a valid selector";
6590                     }
6591                 }
6592                 var result = cache[p](root);
6593                 if(result && result != document){
6594                     results = results.concat(result);
6595                 }
6596             }
6597             if(paths.length > 1){
6598                 return nodup(results);
6599             }
6600             return results;
6601         },
6602
6603         /**
6604          * Selects a single element.
6605          * @param {String} selector The selector/xpath query
6606          * @param {Node} root (optional) The start of the query (defaults to document).
6607          * @return {Element}
6608          */
6609         selectNode : function(path, root){
6610             return Roo.DomQuery.select(path, root)[0];
6611         },
6612
6613         /**
6614          * Selects the value of a node, optionally replacing null with the defaultValue.
6615          * @param {String} selector The selector/xpath query
6616          * @param {Node} root (optional) The start of the query (defaults to document).
6617          * @param {String} defaultValue
6618          */
6619         selectValue : function(path, root, defaultValue){
6620             path = path.replace(trimRe, "");
6621             if(!valueCache[path]){
6622                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6623             }
6624             var n = valueCache[path](root);
6625             n = n[0] ? n[0] : n;
6626             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6627             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6628         },
6629
6630         /**
6631          * Selects the value of a node, parsing integers and floats.
6632          * @param {String} selector The selector/xpath query
6633          * @param {Node} root (optional) The start of the query (defaults to document).
6634          * @param {Number} defaultValue
6635          * @return {Number}
6636          */
6637         selectNumber : function(path, root, defaultValue){
6638             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6639             return parseFloat(v);
6640         },
6641
6642         /**
6643          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6644          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6645          * @param {String} selector The simple selector to test
6646          * @return {Boolean}
6647          */
6648         is : function(el, ss){
6649             if(typeof el == "string"){
6650                 el = document.getElementById(el);
6651             }
6652             var isArray = (el instanceof Array);
6653             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6654             return isArray ? (result.length == el.length) : (result.length > 0);
6655         },
6656
6657         /**
6658          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6659          * @param {Array} el An array of elements to filter
6660          * @param {String} selector The simple selector to test
6661          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6662          * the selector instead of the ones that match
6663          * @return {Array}
6664          */
6665         filter : function(els, ss, nonMatches){
6666             ss = ss.replace(trimRe, "");
6667             if(!simpleCache[ss]){
6668                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6669             }
6670             var result = simpleCache[ss](els);
6671             return nonMatches ? quickDiff(result, els) : result;
6672         },
6673
6674         /**
6675          * Collection of matching regular expressions and code snippets.
6676          */
6677         matchers : [{
6678                 re: /^\.([\w-]+)/,
6679                 select: 'n = byClassName(n, null, " {1} ");'
6680             }, {
6681                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6682                 select: 'n = byPseudo(n, "{1}", "{2}");'
6683             },{
6684                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6685                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6686             }, {
6687                 re: /^#([\w-]+)/,
6688                 select: 'n = byId(n, null, "{1}");'
6689             },{
6690                 re: /^@([\w-]+)/,
6691                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6692             }
6693         ],
6694
6695         /**
6696          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6697          * 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;.
6698          */
6699         operators : {
6700             "=" : function(a, v){
6701                 return a == v;
6702             },
6703             "!=" : function(a, v){
6704                 return a != v;
6705             },
6706             "^=" : function(a, v){
6707                 return a && a.substr(0, v.length) == v;
6708             },
6709             "$=" : function(a, v){
6710                 return a && a.substr(a.length-v.length) == v;
6711             },
6712             "*=" : function(a, v){
6713                 return a && a.indexOf(v) !== -1;
6714             },
6715             "%=" : function(a, v){
6716                 return (a % v) == 0;
6717             },
6718             "|=" : function(a, v){
6719                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6720             },
6721             "~=" : function(a, v){
6722                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6723             }
6724         },
6725
6726         /**
6727          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6728          * and the argument (if any) supplied in the selector.
6729          */
6730         pseudos : {
6731             "first-child" : function(c){
6732                 var r = [], ri = -1, n;
6733                 for(var i = 0, ci; ci = n = c[i]; i++){
6734                     while((n = n.previousSibling) && n.nodeType != 1);
6735                     if(!n){
6736                         r[++ri] = ci;
6737                     }
6738                 }
6739                 return r;
6740             },
6741
6742             "last-child" : function(c){
6743                 var r = [], ri = -1, n;
6744                 for(var i = 0, ci; ci = n = c[i]; i++){
6745                     while((n = n.nextSibling) && n.nodeType != 1);
6746                     if(!n){
6747                         r[++ri] = ci;
6748                     }
6749                 }
6750                 return r;
6751             },
6752
6753             "nth-child" : function(c, a) {
6754                 var r = [], ri = -1;
6755                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6756                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6757                 for(var i = 0, n; n = c[i]; i++){
6758                     var pn = n.parentNode;
6759                     if (batch != pn._batch) {
6760                         var j = 0;
6761                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6762                             if(cn.nodeType == 1){
6763                                cn.nodeIndex = ++j;
6764                             }
6765                         }
6766                         pn._batch = batch;
6767                     }
6768                     if (f == 1) {
6769                         if (l == 0 || n.nodeIndex == l){
6770                             r[++ri] = n;
6771                         }
6772                     } else if ((n.nodeIndex + l) % f == 0){
6773                         r[++ri] = n;
6774                     }
6775                 }
6776
6777                 return r;
6778             },
6779
6780             "only-child" : function(c){
6781                 var r = [], ri = -1;;
6782                 for(var i = 0, ci; ci = c[i]; i++){
6783                     if(!prev(ci) && !next(ci)){
6784                         r[++ri] = ci;
6785                     }
6786                 }
6787                 return r;
6788             },
6789
6790             "empty" : function(c){
6791                 var r = [], ri = -1;
6792                 for(var i = 0, ci; ci = c[i]; i++){
6793                     var cns = ci.childNodes, j = 0, cn, empty = true;
6794                     while(cn = cns[j]){
6795                         ++j;
6796                         if(cn.nodeType == 1 || cn.nodeType == 3){
6797                             empty = false;
6798                             break;
6799                         }
6800                     }
6801                     if(empty){
6802                         r[++ri] = ci;
6803                     }
6804                 }
6805                 return r;
6806             },
6807
6808             "contains" : function(c, v){
6809                 var r = [], ri = -1;
6810                 for(var i = 0, ci; ci = c[i]; i++){
6811                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6812                         r[++ri] = ci;
6813                     }
6814                 }
6815                 return r;
6816             },
6817
6818             "nodeValue" : function(c, v){
6819                 var r = [], ri = -1;
6820                 for(var i = 0, ci; ci = c[i]; i++){
6821                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6822                         r[++ri] = ci;
6823                     }
6824                 }
6825                 return r;
6826             },
6827
6828             "checked" : function(c){
6829                 var r = [], ri = -1;
6830                 for(var i = 0, ci; ci = c[i]; i++){
6831                     if(ci.checked == true){
6832                         r[++ri] = ci;
6833                     }
6834                 }
6835                 return r;
6836             },
6837
6838             "not" : function(c, ss){
6839                 return Roo.DomQuery.filter(c, ss, true);
6840             },
6841
6842             "odd" : function(c){
6843                 return this["nth-child"](c, "odd");
6844             },
6845
6846             "even" : function(c){
6847                 return this["nth-child"](c, "even");
6848             },
6849
6850             "nth" : function(c, a){
6851                 return c[a-1] || [];
6852             },
6853
6854             "first" : function(c){
6855                 return c[0] || [];
6856             },
6857
6858             "last" : function(c){
6859                 return c[c.length-1] || [];
6860             },
6861
6862             "has" : function(c, ss){
6863                 var s = Roo.DomQuery.select;
6864                 var r = [], ri = -1;
6865                 for(var i = 0, ci; ci = c[i]; i++){
6866                     if(s(ss, ci).length > 0){
6867                         r[++ri] = ci;
6868                     }
6869                 }
6870                 return r;
6871             },
6872
6873             "next" : function(c, ss){
6874                 var is = Roo.DomQuery.is;
6875                 var r = [], ri = -1;
6876                 for(var i = 0, ci; ci = c[i]; i++){
6877                     var n = next(ci);
6878                     if(n && is(n, ss)){
6879                         r[++ri] = ci;
6880                     }
6881                 }
6882                 return r;
6883             },
6884
6885             "prev" : function(c, ss){
6886                 var is = Roo.DomQuery.is;
6887                 var r = [], ri = -1;
6888                 for(var i = 0, ci; ci = c[i]; i++){
6889                     var n = prev(ci);
6890                     if(n && is(n, ss)){
6891                         r[++ri] = ci;
6892                     }
6893                 }
6894                 return r;
6895             }
6896         }
6897     };
6898 }();
6899
6900 /**
6901  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6902  * @param {String} path The selector/xpath query
6903  * @param {Node} root (optional) The start of the query (defaults to document).
6904  * @return {Array}
6905  * @member Roo
6906  * @method query
6907  */
6908 Roo.query = Roo.DomQuery.select;
6909 /*
6910  * Based on:
6911  * Ext JS Library 1.1.1
6912  * Copyright(c) 2006-2007, Ext JS, LLC.
6913  *
6914  * Originally Released Under LGPL - original licence link has changed is not relivant.
6915  *
6916  * Fork - LGPL
6917  * <script type="text/javascript">
6918  */
6919
6920 /**
6921  * @class Roo.util.Observable
6922  * Base class that provides a common interface for publishing events. Subclasses are expected to
6923  * to have a property "events" with all the events defined.<br>
6924  * For example:
6925  * <pre><code>
6926  Employee = function(name){
6927     this.name = name;
6928     this.addEvents({
6929         "fired" : true,
6930         "quit" : true
6931     });
6932  }
6933  Roo.extend(Employee, Roo.util.Observable);
6934 </code></pre>
6935  * @param {Object} config properties to use (incuding events / listeners)
6936  */
6937
6938 Roo.util.Observable = function(cfg){
6939     
6940     cfg = cfg|| {};
6941     this.addEvents(cfg.events || {});
6942     if (cfg.events) {
6943         delete cfg.events; // make sure
6944     }
6945      
6946     Roo.apply(this, cfg);
6947     
6948     if(this.listeners){
6949         this.on(this.listeners);
6950         delete this.listeners;
6951     }
6952 };
6953 Roo.util.Observable.prototype = {
6954     /** 
6955  * @cfg {Object} listeners  list of events and functions to call for this object, 
6956  * For example :
6957  * <pre><code>
6958     listeners :  { 
6959        'click' : function(e) {
6960            ..... 
6961         } ,
6962         .... 
6963     } 
6964   </code></pre>
6965  */
6966     
6967     
6968     /**
6969      * Fires the specified event with the passed parameters (minus the event name).
6970      * @param {String} eventName
6971      * @param {Object...} args Variable number of parameters are passed to handlers
6972      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6973      */
6974     fireEvent : function(){
6975         var ce = this.events[arguments[0].toLowerCase()];
6976         if(typeof ce == "object"){
6977             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6978         }else{
6979             return true;
6980         }
6981     },
6982
6983     // private
6984     filterOptRe : /^(?:scope|delay|buffer|single)$/,
6985
6986     /**
6987      * Appends an event handler to this component
6988      * @param {String}   eventName The type of event to listen for
6989      * @param {Function} handler The method the event invokes
6990      * @param {Object}   scope (optional) The scope in which to execute the handler
6991      * function. The handler function's "this" context.
6992      * @param {Object}   options (optional) An object containing handler configuration
6993      * properties. This may contain any of the following properties:<ul>
6994      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6995      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6996      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6997      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6998      * by the specified number of milliseconds. If the event fires again within that time, the original
6999      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7000      * </ul><br>
7001      * <p>
7002      * <b>Combining Options</b><br>
7003      * Using the options argument, it is possible to combine different types of listeners:<br>
7004      * <br>
7005      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
7006                 <pre><code>
7007                 el.on('click', this.onClick, this, {
7008                         single: true,
7009                 delay: 100,
7010                 forumId: 4
7011                 });
7012                 </code></pre>
7013      * <p>
7014      * <b>Attaching multiple handlers in 1 call</b><br>
7015      * The method also allows for a single argument to be passed which is a config object containing properties
7016      * which specify multiple handlers.
7017      * <pre><code>
7018                 el.on({
7019                         'click': {
7020                         fn: this.onClick,
7021                         scope: this,
7022                         delay: 100
7023                 }, 
7024                 'mouseover': {
7025                         fn: this.onMouseOver,
7026                         scope: this
7027                 },
7028                 'mouseout': {
7029                         fn: this.onMouseOut,
7030                         scope: this
7031                 }
7032                 });
7033                 </code></pre>
7034      * <p>
7035      * Or a shorthand syntax which passes the same scope object to all handlers:
7036         <pre><code>
7037                 el.on({
7038                         'click': this.onClick,
7039                 'mouseover': this.onMouseOver,
7040                 'mouseout': this.onMouseOut,
7041                 scope: this
7042                 });
7043                 </code></pre>
7044      */
7045     addListener : function(eventName, fn, scope, o){
7046         if(typeof eventName == "object"){
7047             o = eventName;
7048             for(var e in o){
7049                 if(this.filterOptRe.test(e)){
7050                     continue;
7051                 }
7052                 if(typeof o[e] == "function"){
7053                     // shared options
7054                     this.addListener(e, o[e], o.scope,  o);
7055                 }else{
7056                     // individual options
7057                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
7058                 }
7059             }
7060             return;
7061         }
7062         o = (!o || typeof o == "boolean") ? {} : o;
7063         eventName = eventName.toLowerCase();
7064         var ce = this.events[eventName] || true;
7065         if(typeof ce == "boolean"){
7066             ce = new Roo.util.Event(this, eventName);
7067             this.events[eventName] = ce;
7068         }
7069         ce.addListener(fn, scope, o);
7070     },
7071
7072     /**
7073      * Removes a listener
7074      * @param {String}   eventName     The type of event to listen for
7075      * @param {Function} handler        The handler to remove
7076      * @param {Object}   scope  (optional) The scope (this object) for the handler
7077      */
7078     removeListener : function(eventName, fn, scope){
7079         var ce = this.events[eventName.toLowerCase()];
7080         if(typeof ce == "object"){
7081             ce.removeListener(fn, scope);
7082         }
7083     },
7084
7085     /**
7086      * Removes all listeners for this object
7087      */
7088     purgeListeners : function(){
7089         for(var evt in this.events){
7090             if(typeof this.events[evt] == "object"){
7091                  this.events[evt].clearListeners();
7092             }
7093         }
7094     },
7095
7096     relayEvents : function(o, events){
7097         var createHandler = function(ename){
7098             return function(){
7099                  
7100                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
7101             };
7102         };
7103         for(var i = 0, len = events.length; i < len; i++){
7104             var ename = events[i];
7105             if(!this.events[ename]){
7106                 this.events[ename] = true;
7107             };
7108             o.on(ename, createHandler(ename), this);
7109         }
7110     },
7111
7112     /**
7113      * Used to define events on this Observable
7114      * @param {Object} object The object with the events defined
7115      */
7116     addEvents : function(o){
7117         if(!this.events){
7118             this.events = {};
7119         }
7120         Roo.applyIf(this.events, o);
7121     },
7122
7123     /**
7124      * Checks to see if this object has any listeners for a specified event
7125      * @param {String} eventName The name of the event to check for
7126      * @return {Boolean} True if the event is being listened for, else false
7127      */
7128     hasListener : function(eventName){
7129         var e = this.events[eventName];
7130         return typeof e == "object" && e.listeners.length > 0;
7131     }
7132 };
7133 /**
7134  * Appends an event handler to this element (shorthand for addListener)
7135  * @param {String}   eventName     The type of event to listen for
7136  * @param {Function} handler        The method the event invokes
7137  * @param {Object}   scope (optional) The scope in which to execute the handler
7138  * function. The handler function's "this" context.
7139  * @param {Object}   options  (optional)
7140  * @method
7141  */
7142 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
7143 /**
7144  * Removes a listener (shorthand for removeListener)
7145  * @param {String}   eventName     The type of event to listen for
7146  * @param {Function} handler        The handler to remove
7147  * @param {Object}   scope  (optional) The scope (this object) for the handler
7148  * @method
7149  */
7150 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
7151
7152 /**
7153  * Starts capture on the specified Observable. All events will be passed
7154  * to the supplied function with the event name + standard signature of the event
7155  * <b>before</b> the event is fired. If the supplied function returns false,
7156  * the event will not fire.
7157  * @param {Observable} o The Observable to capture
7158  * @param {Function} fn The function to call
7159  * @param {Object} scope (optional) The scope (this object) for the fn
7160  * @static
7161  */
7162 Roo.util.Observable.capture = function(o, fn, scope){
7163     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
7164 };
7165
7166 /**
7167  * Removes <b>all</b> added captures from the Observable.
7168  * @param {Observable} o The Observable to release
7169  * @static
7170  */
7171 Roo.util.Observable.releaseCapture = function(o){
7172     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
7173 };
7174
7175 (function(){
7176
7177     var createBuffered = function(h, o, scope){
7178         var task = new Roo.util.DelayedTask();
7179         return function(){
7180             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
7181         };
7182     };
7183
7184     var createSingle = function(h, e, fn, scope){
7185         return function(){
7186             e.removeListener(fn, scope);
7187             return h.apply(scope, arguments);
7188         };
7189     };
7190
7191     var createDelayed = function(h, o, scope){
7192         return function(){
7193             var args = Array.prototype.slice.call(arguments, 0);
7194             setTimeout(function(){
7195                 h.apply(scope, args);
7196             }, o.delay || 10);
7197         };
7198     };
7199
7200     Roo.util.Event = function(obj, name){
7201         this.name = name;
7202         this.obj = obj;
7203         this.listeners = [];
7204     };
7205
7206     Roo.util.Event.prototype = {
7207         addListener : function(fn, scope, options){
7208             var o = options || {};
7209             scope = scope || this.obj;
7210             if(!this.isListening(fn, scope)){
7211                 var l = {fn: fn, scope: scope, options: o};
7212                 var h = fn;
7213                 if(o.delay){
7214                     h = createDelayed(h, o, scope);
7215                 }
7216                 if(o.single){
7217                     h = createSingle(h, this, fn, scope);
7218                 }
7219                 if(o.buffer){
7220                     h = createBuffered(h, o, scope);
7221                 }
7222                 l.fireFn = h;
7223                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
7224                     this.listeners.push(l);
7225                 }else{
7226                     this.listeners = this.listeners.slice(0);
7227                     this.listeners.push(l);
7228                 }
7229             }
7230         },
7231
7232         findListener : function(fn, scope){
7233             scope = scope || this.obj;
7234             var ls = this.listeners;
7235             for(var i = 0, len = ls.length; i < len; i++){
7236                 var l = ls[i];
7237                 if(l.fn == fn && l.scope == scope){
7238                     return i;
7239                 }
7240             }
7241             return -1;
7242         },
7243
7244         isListening : function(fn, scope){
7245             return this.findListener(fn, scope) != -1;
7246         },
7247
7248         removeListener : function(fn, scope){
7249             var index;
7250             if((index = this.findListener(fn, scope)) != -1){
7251                 if(!this.firing){
7252                     this.listeners.splice(index, 1);
7253                 }else{
7254                     this.listeners = this.listeners.slice(0);
7255                     this.listeners.splice(index, 1);
7256                 }
7257                 return true;
7258             }
7259             return false;
7260         },
7261
7262         clearListeners : function(){
7263             this.listeners = [];
7264         },
7265
7266         fire : function(){
7267             var ls = this.listeners, scope, len = ls.length;
7268             if(len > 0){
7269                 this.firing = true;
7270                 var args = Array.prototype.slice.call(arguments, 0);                
7271                 for(var i = 0; i < len; i++){
7272                     var l = ls[i];
7273                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
7274                         this.firing = false;
7275                         return false;
7276                     }
7277                 }
7278                 this.firing = false;
7279             }
7280             return true;
7281         }
7282     };
7283 })();/*
7284  * RooJS Library 
7285  * Copyright(c) 2007-2017, Roo J Solutions Ltd
7286  *
7287  * Licence LGPL 
7288  *
7289  */
7290  
7291 /**
7292  * @class Roo.Document
7293  * @extends Roo.util.Observable
7294  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
7295  * 
7296  * @param {Object} config the methods and properties of the 'base' class for the application.
7297  * 
7298  *  Generic Page handler - implement this to start your app..
7299  * 
7300  * eg.
7301  *  MyProject = new Roo.Document({
7302         events : {
7303             'load' : true // your events..
7304         },
7305         listeners : {
7306             'ready' : function() {
7307                 // fired on Roo.onReady()
7308             }
7309         }
7310  * 
7311  */
7312 Roo.Document = function(cfg) {
7313      
7314     this.addEvents({ 
7315         'ready' : true
7316     });
7317     Roo.util.Observable.call(this,cfg);
7318     
7319     var _this = this;
7320     
7321     Roo.onReady(function() {
7322         _this.fireEvent('ready');
7323     },null,false);
7324     
7325     
7326 }
7327
7328 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7329  * Based on:
7330  * Ext JS Library 1.1.1
7331  * Copyright(c) 2006-2007, Ext JS, LLC.
7332  *
7333  * Originally Released Under LGPL - original licence link has changed is not relivant.
7334  *
7335  * Fork - LGPL
7336  * <script type="text/javascript">
7337  */
7338
7339 /**
7340  * @class Roo.EventManager
7341  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7342  * several useful events directly.
7343  * See {@link Roo.EventObject} for more details on normalized event objects.
7344  * @static
7345  */
7346 Roo.EventManager = function(){
7347     var docReadyEvent, docReadyProcId, docReadyState = false;
7348     var resizeEvent, resizeTask, textEvent, textSize;
7349     var E = Roo.lib.Event;
7350     var D = Roo.lib.Dom;
7351
7352     
7353     
7354
7355     var fireDocReady = function(){
7356         if(!docReadyState){
7357             docReadyState = true;
7358             Roo.isReady = true;
7359             if(docReadyProcId){
7360                 clearInterval(docReadyProcId);
7361             }
7362             if(Roo.isGecko || Roo.isOpera) {
7363                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7364             }
7365             if(Roo.isIE){
7366                 var defer = document.getElementById("ie-deferred-loader");
7367                 if(defer){
7368                     defer.onreadystatechange = null;
7369                     defer.parentNode.removeChild(defer);
7370                 }
7371             }
7372             if(docReadyEvent){
7373                 docReadyEvent.fire();
7374                 docReadyEvent.clearListeners();
7375             }
7376         }
7377     };
7378     
7379     var initDocReady = function(){
7380         docReadyEvent = new Roo.util.Event();
7381         if(Roo.isGecko || Roo.isOpera) {
7382             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7383         }else if(Roo.isIE){
7384             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7385             var defer = document.getElementById("ie-deferred-loader");
7386             defer.onreadystatechange = function(){
7387                 if(this.readyState == "complete"){
7388                     fireDocReady();
7389                 }
7390             };
7391         }else if(Roo.isSafari){ 
7392             docReadyProcId = setInterval(function(){
7393                 var rs = document.readyState;
7394                 if(rs == "complete") {
7395                     fireDocReady();     
7396                  }
7397             }, 10);
7398         }
7399         // no matter what, make sure it fires on load
7400         E.on(window, "load", fireDocReady);
7401     };
7402
7403     var createBuffered = function(h, o){
7404         var task = new Roo.util.DelayedTask(h);
7405         return function(e){
7406             // create new event object impl so new events don't wipe out properties
7407             e = new Roo.EventObjectImpl(e);
7408             task.delay(o.buffer, h, null, [e]);
7409         };
7410     };
7411
7412     var createSingle = function(h, el, ename, fn){
7413         return function(e){
7414             Roo.EventManager.removeListener(el, ename, fn);
7415             h(e);
7416         };
7417     };
7418
7419     var createDelayed = function(h, o){
7420         return function(e){
7421             // create new event object impl so new events don't wipe out properties
7422             e = new Roo.EventObjectImpl(e);
7423             setTimeout(function(){
7424                 h(e);
7425             }, o.delay || 10);
7426         };
7427     };
7428     var transitionEndVal = false;
7429     
7430     var transitionEnd = function()
7431     {
7432         if (transitionEndVal) {
7433             return transitionEndVal;
7434         }
7435         var el = document.createElement('div');
7436
7437         var transEndEventNames = {
7438             WebkitTransition : 'webkitTransitionEnd',
7439             MozTransition    : 'transitionend',
7440             OTransition      : 'oTransitionEnd otransitionend',
7441             transition       : 'transitionend'
7442         };
7443     
7444         for (var name in transEndEventNames) {
7445             if (el.style[name] !== undefined) {
7446                 transitionEndVal = transEndEventNames[name];
7447                 return  transitionEndVal ;
7448             }
7449         }
7450     }
7451     
7452   
7453
7454     var listen = function(element, ename, opt, fn, scope)
7455     {
7456         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7457         fn = fn || o.fn; scope = scope || o.scope;
7458         var el = Roo.getDom(element);
7459         
7460         
7461         if(!el){
7462             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7463         }
7464         
7465         if (ename == 'transitionend') {
7466             ename = transitionEnd();
7467         }
7468         var h = function(e){
7469             e = Roo.EventObject.setEvent(e);
7470             var t;
7471             if(o.delegate){
7472                 t = e.getTarget(o.delegate, el);
7473                 if(!t){
7474                     return;
7475                 }
7476             }else{
7477                 t = e.target;
7478             }
7479             if(o.stopEvent === true){
7480                 e.stopEvent();
7481             }
7482             if(o.preventDefault === true){
7483                e.preventDefault();
7484             }
7485             if(o.stopPropagation === true){
7486                 e.stopPropagation();
7487             }
7488
7489             if(o.normalized === false){
7490                 e = e.browserEvent;
7491             }
7492
7493             fn.call(scope || el, e, t, o);
7494         };
7495         if(o.delay){
7496             h = createDelayed(h, o);
7497         }
7498         if(o.single){
7499             h = createSingle(h, el, ename, fn);
7500         }
7501         if(o.buffer){
7502             h = createBuffered(h, o);
7503         }
7504         
7505         fn._handlers = fn._handlers || [];
7506         
7507         
7508         fn._handlers.push([Roo.id(el), ename, h]);
7509         
7510         
7511          
7512         E.on(el, ename, h); // this adds the actuall listener to the object..
7513         
7514         
7515         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7516             el.addEventListener("DOMMouseScroll", h, false);
7517             E.on(window, 'unload', function(){
7518                 el.removeEventListener("DOMMouseScroll", h, false);
7519             });
7520         }
7521         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7522             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7523         }
7524         return h;
7525     };
7526
7527     var stopListening = function(el, ename, fn){
7528         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7529         if(hds){
7530             for(var i = 0, len = hds.length; i < len; i++){
7531                 var h = hds[i];
7532                 if(h[0] == id && h[1] == ename){
7533                     hd = h[2];
7534                     hds.splice(i, 1);
7535                     break;
7536                 }
7537             }
7538         }
7539         E.un(el, ename, hd);
7540         el = Roo.getDom(el);
7541         if(ename == "mousewheel" && el.addEventListener){
7542             el.removeEventListener("DOMMouseScroll", hd, false);
7543         }
7544         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7545             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7546         }
7547     };
7548
7549     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7550     
7551     var pub = {
7552         
7553         
7554         /** 
7555          * Fix for doc tools
7556          * @scope Roo.EventManager
7557          */
7558         
7559         
7560         /** 
7561          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7562          * object with a Roo.EventObject
7563          * @param {Function} fn        The method the event invokes
7564          * @param {Object}   scope    An object that becomes the scope of the handler
7565          * @param {boolean}  override If true, the obj passed in becomes
7566          *                             the execution scope of the listener
7567          * @return {Function} The wrapped function
7568          * @deprecated
7569          */
7570         wrap : function(fn, scope, override){
7571             return function(e){
7572                 Roo.EventObject.setEvent(e);
7573                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7574             };
7575         },
7576         
7577         /**
7578      * Appends an event handler to an element (shorthand for addListener)
7579      * @param {String/HTMLElement}   element        The html element or id to assign the
7580      * @param {String}   eventName The type of event to listen for
7581      * @param {Function} handler The method the event invokes
7582      * @param {Object}   scope (optional) The scope in which to execute the handler
7583      * function. The handler function's "this" context.
7584      * @param {Object}   options (optional) An object containing handler configuration
7585      * properties. This may contain any of the following properties:<ul>
7586      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7587      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7588      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7589      * <li>preventDefault {Boolean} True to prevent the default action</li>
7590      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7591      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7592      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7593      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7594      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7595      * by the specified number of milliseconds. If the event fires again within that time, the original
7596      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7597      * </ul><br>
7598      * <p>
7599      * <b>Combining Options</b><br>
7600      * Using the options argument, it is possible to combine different types of listeners:<br>
7601      * <br>
7602      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7603      * Code:<pre><code>
7604 el.on('click', this.onClick, this, {
7605     single: true,
7606     delay: 100,
7607     stopEvent : true,
7608     forumId: 4
7609 });</code></pre>
7610      * <p>
7611      * <b>Attaching multiple handlers in 1 call</b><br>
7612       * The method also allows for a single argument to be passed which is a config object containing properties
7613      * which specify multiple handlers.
7614      * <p>
7615      * Code:<pre><code>
7616 el.on({
7617     'click' : {
7618         fn: this.onClick
7619         scope: this,
7620         delay: 100
7621     },
7622     'mouseover' : {
7623         fn: this.onMouseOver
7624         scope: this
7625     },
7626     'mouseout' : {
7627         fn: this.onMouseOut
7628         scope: this
7629     }
7630 });</code></pre>
7631      * <p>
7632      * Or a shorthand syntax:<br>
7633      * Code:<pre><code>
7634 el.on({
7635     'click' : this.onClick,
7636     'mouseover' : this.onMouseOver,
7637     'mouseout' : this.onMouseOut
7638     scope: this
7639 });</code></pre>
7640      */
7641         addListener : function(element, eventName, fn, scope, options){
7642             if(typeof eventName == "object"){
7643                 var o = eventName;
7644                 for(var e in o){
7645                     if(propRe.test(e)){
7646                         continue;
7647                     }
7648                     if(typeof o[e] == "function"){
7649                         // shared options
7650                         listen(element, e, o, o[e], o.scope);
7651                     }else{
7652                         // individual options
7653                         listen(element, e, o[e]);
7654                     }
7655                 }
7656                 return;
7657             }
7658             return listen(element, eventName, options, fn, scope);
7659         },
7660         
7661         /**
7662          * Removes an event handler
7663          *
7664          * @param {String/HTMLElement}   element        The id or html element to remove the 
7665          *                             event from
7666          * @param {String}   eventName     The type of event
7667          * @param {Function} fn
7668          * @return {Boolean} True if a listener was actually removed
7669          */
7670         removeListener : function(element, eventName, fn){
7671             return stopListening(element, eventName, fn);
7672         },
7673         
7674         /**
7675          * Fires when the document is ready (before onload and before images are loaded). Can be 
7676          * accessed shorthanded Roo.onReady().
7677          * @param {Function} fn        The method the event invokes
7678          * @param {Object}   scope    An  object that becomes the scope of the handler
7679          * @param {boolean}  options
7680          */
7681         onDocumentReady : function(fn, scope, options){
7682             if(docReadyState){ // if it already fired
7683                 docReadyEvent.addListener(fn, scope, options);
7684                 docReadyEvent.fire();
7685                 docReadyEvent.clearListeners();
7686                 return;
7687             }
7688             if(!docReadyEvent){
7689                 initDocReady();
7690             }
7691             docReadyEvent.addListener(fn, scope, options);
7692         },
7693         
7694         /**
7695          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7696          * @param {Function} fn        The method the event invokes
7697          * @param {Object}   scope    An object that becomes the scope of the handler
7698          * @param {boolean}  options
7699          */
7700         onWindowResize : function(fn, scope, options)
7701         {
7702             if(!resizeEvent){
7703                 resizeEvent = new Roo.util.Event();
7704                 resizeTask = new Roo.util.DelayedTask(function(){
7705                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7706                 });
7707                 E.on(window, "resize", function()
7708                 {
7709                     if (Roo.isIE) {
7710                         resizeTask.delay(50);
7711                     } else {
7712                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7713                     }
7714                 });
7715             }
7716             resizeEvent.addListener(fn, scope, options);
7717         },
7718
7719         /**
7720          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7721          * @param {Function} fn        The method the event invokes
7722          * @param {Object}   scope    An object that becomes the scope of the handler
7723          * @param {boolean}  options
7724          */
7725         onTextResize : function(fn, scope, options){
7726             if(!textEvent){
7727                 textEvent = new Roo.util.Event();
7728                 var textEl = new Roo.Element(document.createElement('div'));
7729                 textEl.dom.className = 'x-text-resize';
7730                 textEl.dom.innerHTML = 'X';
7731                 textEl.appendTo(document.body);
7732                 textSize = textEl.dom.offsetHeight;
7733                 setInterval(function(){
7734                     if(textEl.dom.offsetHeight != textSize){
7735                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7736                     }
7737                 }, this.textResizeInterval);
7738             }
7739             textEvent.addListener(fn, scope, options);
7740         },
7741
7742         /**
7743          * Removes the passed window resize listener.
7744          * @param {Function} fn        The method the event invokes
7745          * @param {Object}   scope    The scope of handler
7746          */
7747         removeResizeListener : function(fn, scope){
7748             if(resizeEvent){
7749                 resizeEvent.removeListener(fn, scope);
7750             }
7751         },
7752
7753         // private
7754         fireResize : function(){
7755             if(resizeEvent){
7756                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7757             }   
7758         },
7759         /**
7760          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7761          */
7762         ieDeferSrc : false,
7763         /**
7764          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7765          */
7766         textResizeInterval : 50
7767     };
7768     
7769     /**
7770      * Fix for doc tools
7771      * @scopeAlias pub=Roo.EventManager
7772      */
7773     
7774      /**
7775      * Appends an event handler to an element (shorthand for addListener)
7776      * @param {String/HTMLElement}   element        The html element or id to assign the
7777      * @param {String}   eventName The type of event to listen for
7778      * @param {Function} handler The method the event invokes
7779      * @param {Object}   scope (optional) The scope in which to execute the handler
7780      * function. The handler function's "this" context.
7781      * @param {Object}   options (optional) An object containing handler configuration
7782      * properties. This may contain any of the following properties:<ul>
7783      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7784      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7785      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7786      * <li>preventDefault {Boolean} True to prevent the default action</li>
7787      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7788      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7789      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7790      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7791      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7792      * by the specified number of milliseconds. If the event fires again within that time, the original
7793      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7794      * </ul><br>
7795      * <p>
7796      * <b>Combining Options</b><br>
7797      * Using the options argument, it is possible to combine different types of listeners:<br>
7798      * <br>
7799      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7800      * Code:<pre><code>
7801 el.on('click', this.onClick, this, {
7802     single: true,
7803     delay: 100,
7804     stopEvent : true,
7805     forumId: 4
7806 });</code></pre>
7807      * <p>
7808      * <b>Attaching multiple handlers in 1 call</b><br>
7809       * The method also allows for a single argument to be passed which is a config object containing properties
7810      * which specify multiple handlers.
7811      * <p>
7812      * Code:<pre><code>
7813 el.on({
7814     'click' : {
7815         fn: this.onClick
7816         scope: this,
7817         delay: 100
7818     },
7819     'mouseover' : {
7820         fn: this.onMouseOver
7821         scope: this
7822     },
7823     'mouseout' : {
7824         fn: this.onMouseOut
7825         scope: this
7826     }
7827 });</code></pre>
7828      * <p>
7829      * Or a shorthand syntax:<br>
7830      * Code:<pre><code>
7831 el.on({
7832     'click' : this.onClick,
7833     'mouseover' : this.onMouseOver,
7834     'mouseout' : this.onMouseOut
7835     scope: this
7836 });</code></pre>
7837      */
7838     pub.on = pub.addListener;
7839     pub.un = pub.removeListener;
7840
7841     pub.stoppedMouseDownEvent = new Roo.util.Event();
7842     return pub;
7843 }();
7844 /**
7845   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
7846   * @param {Function} fn        The method the event invokes
7847   * @param {Object}   scope    An  object that becomes the scope of the handler
7848   * @param {boolean}  override If true, the obj passed in becomes
7849   *                             the execution scope of the listener
7850   * @member Roo
7851   * @method onReady
7852  */
7853 Roo.onReady = Roo.EventManager.onDocumentReady;
7854
7855 Roo.onReady(function(){
7856     var bd = Roo.get(document.body);
7857     if(!bd){ return; }
7858
7859     var cls = [
7860             Roo.isIE ? "roo-ie"
7861             : Roo.isIE11 ? "roo-ie11"
7862             : Roo.isEdge ? "roo-edge"
7863             : Roo.isGecko ? "roo-gecko"
7864             : Roo.isOpera ? "roo-opera"
7865             : Roo.isSafari ? "roo-safari" : ""];
7866
7867     if(Roo.isMac){
7868         cls.push("roo-mac");
7869     }
7870     if(Roo.isLinux){
7871         cls.push("roo-linux");
7872     }
7873     if(Roo.isIOS){
7874         cls.push("roo-ios");
7875     }
7876     if(Roo.isTouch){
7877         cls.push("roo-touch");
7878     }
7879     if(Roo.isBorderBox){
7880         cls.push('roo-border-box');
7881     }
7882     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7883         var p = bd.dom.parentNode;
7884         if(p){
7885             p.className += ' roo-strict';
7886         }
7887     }
7888     bd.addClass(cls.join(' '));
7889 });
7890
7891 /**
7892  * @class Roo.EventObject
7893  * EventObject exposes the Yahoo! UI Event functionality directly on the object
7894  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
7895  * Example:
7896  * <pre><code>
7897  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7898     e.preventDefault();
7899     var target = e.getTarget();
7900     ...
7901  }
7902  var myDiv = Roo.get("myDiv");
7903  myDiv.on("click", handleClick);
7904  //or
7905  Roo.EventManager.on("myDiv", 'click', handleClick);
7906  Roo.EventManager.addListener("myDiv", 'click', handleClick);
7907  </code></pre>
7908  * @static
7909  */
7910 Roo.EventObject = function(){
7911     
7912     var E = Roo.lib.Event;
7913     
7914     // safari keypress events for special keys return bad keycodes
7915     var safariKeys = {
7916         63234 : 37, // left
7917         63235 : 39, // right
7918         63232 : 38, // up
7919         63233 : 40, // down
7920         63276 : 33, // page up
7921         63277 : 34, // page down
7922         63272 : 46, // delete
7923         63273 : 36, // home
7924         63275 : 35  // end
7925     };
7926
7927     // normalize button clicks
7928     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7929                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7930
7931     Roo.EventObjectImpl = function(e){
7932         if(e){
7933             this.setEvent(e.browserEvent || e);
7934         }
7935     };
7936     Roo.EventObjectImpl.prototype = {
7937         /**
7938          * Used to fix doc tools.
7939          * @scope Roo.EventObject.prototype
7940          */
7941             
7942
7943         
7944         
7945         /** The normal browser event */
7946         browserEvent : null,
7947         /** The button pressed in a mouse event */
7948         button : -1,
7949         /** True if the shift key was down during the event */
7950         shiftKey : false,
7951         /** True if the control key was down during the event */
7952         ctrlKey : false,
7953         /** True if the alt key was down during the event */
7954         altKey : false,
7955
7956         /** Key constant 
7957         * @type Number */
7958         BACKSPACE : 8,
7959         /** Key constant 
7960         * @type Number */
7961         TAB : 9,
7962         /** Key constant 
7963         * @type Number */
7964         RETURN : 13,
7965         /** Key constant 
7966         * @type Number */
7967         ENTER : 13,
7968         /** Key constant 
7969         * @type Number */
7970         SHIFT : 16,
7971         /** Key constant 
7972         * @type Number */
7973         CONTROL : 17,
7974         /** Key constant 
7975         * @type Number */
7976         ESC : 27,
7977         /** Key constant 
7978         * @type Number */
7979         SPACE : 32,
7980         /** Key constant 
7981         * @type Number */
7982         PAGEUP : 33,
7983         /** Key constant 
7984         * @type Number */
7985         PAGEDOWN : 34,
7986         /** Key constant 
7987         * @type Number */
7988         END : 35,
7989         /** Key constant 
7990         * @type Number */
7991         HOME : 36,
7992         /** Key constant 
7993         * @type Number */
7994         LEFT : 37,
7995         /** Key constant 
7996         * @type Number */
7997         UP : 38,
7998         /** Key constant 
7999         * @type Number */
8000         RIGHT : 39,
8001         /** Key constant 
8002         * @type Number */
8003         DOWN : 40,
8004         /** Key constant 
8005         * @type Number */
8006         DELETE : 46,
8007         /** Key constant 
8008         * @type Number */
8009         F5 : 116,
8010
8011            /** @private */
8012         setEvent : function(e){
8013             if(e == this || (e && e.browserEvent)){ // already wrapped
8014                 return e;
8015             }
8016             this.browserEvent = e;
8017             if(e){
8018                 // normalize buttons
8019                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
8020                 if(e.type == 'click' && this.button == -1){
8021                     this.button = 0;
8022                 }
8023                 this.type = e.type;
8024                 this.shiftKey = e.shiftKey;
8025                 // mac metaKey behaves like ctrlKey
8026                 this.ctrlKey = e.ctrlKey || e.metaKey;
8027                 this.altKey = e.altKey;
8028                 // in getKey these will be normalized for the mac
8029                 this.keyCode = e.keyCode;
8030                 // keyup warnings on firefox.
8031                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
8032                 // cache the target for the delayed and or buffered events
8033                 this.target = E.getTarget(e);
8034                 // same for XY
8035                 this.xy = E.getXY(e);
8036             }else{
8037                 this.button = -1;
8038                 this.shiftKey = false;
8039                 this.ctrlKey = false;
8040                 this.altKey = false;
8041                 this.keyCode = 0;
8042                 this.charCode =0;
8043                 this.target = null;
8044                 this.xy = [0, 0];
8045             }
8046             return this;
8047         },
8048
8049         /**
8050          * Stop the event (preventDefault and stopPropagation)
8051          */
8052         stopEvent : function(){
8053             if(this.browserEvent){
8054                 if(this.browserEvent.type == 'mousedown'){
8055                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8056                 }
8057                 E.stopEvent(this.browserEvent);
8058             }
8059         },
8060
8061         /**
8062          * Prevents the browsers default handling of the event.
8063          */
8064         preventDefault : function(){
8065             if(this.browserEvent){
8066                 E.preventDefault(this.browserEvent);
8067             }
8068         },
8069
8070         /** @private */
8071         isNavKeyPress : function(){
8072             var k = this.keyCode;
8073             k = Roo.isSafari ? (safariKeys[k] || k) : k;
8074             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
8075         },
8076
8077         isSpecialKey : function(){
8078             var k = this.keyCode;
8079             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
8080             (k == 16) || (k == 17) ||
8081             (k >= 18 && k <= 20) ||
8082             (k >= 33 && k <= 35) ||
8083             (k >= 36 && k <= 39) ||
8084             (k >= 44 && k <= 45);
8085         },
8086         /**
8087          * Cancels bubbling of the event.
8088          */
8089         stopPropagation : function(){
8090             if(this.browserEvent){
8091                 if(this.type == 'mousedown'){
8092                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8093                 }
8094                 E.stopPropagation(this.browserEvent);
8095             }
8096         },
8097
8098         /**
8099          * Gets the key code for the event.
8100          * @return {Number}
8101          */
8102         getCharCode : function(){
8103             return this.charCode || this.keyCode;
8104         },
8105
8106         /**
8107          * Returns a normalized keyCode for the event.
8108          * @return {Number} The key code
8109          */
8110         getKey : function(){
8111             var k = this.keyCode || this.charCode;
8112             return Roo.isSafari ? (safariKeys[k] || k) : k;
8113         },
8114
8115         /**
8116          * Gets the x coordinate of the event.
8117          * @return {Number}
8118          */
8119         getPageX : function(){
8120             return this.xy[0];
8121         },
8122
8123         /**
8124          * Gets the y coordinate of the event.
8125          * @return {Number}
8126          */
8127         getPageY : function(){
8128             return this.xy[1];
8129         },
8130
8131         /**
8132          * Gets the time of the event.
8133          * @return {Number}
8134          */
8135         getTime : function(){
8136             if(this.browserEvent){
8137                 return E.getTime(this.browserEvent);
8138             }
8139             return null;
8140         },
8141
8142         /**
8143          * Gets the page coordinates of the event.
8144          * @return {Array} The xy values like [x, y]
8145          */
8146         getXY : function(){
8147             return this.xy;
8148         },
8149
8150         /**
8151          * Gets the target for the event.
8152          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
8153          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8154                 search as a number or element (defaults to 10 || document.body)
8155          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8156          * @return {HTMLelement}
8157          */
8158         getTarget : function(selector, maxDepth, returnEl){
8159             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
8160         },
8161         /**
8162          * Gets the related target.
8163          * @return {HTMLElement}
8164          */
8165         getRelatedTarget : function(){
8166             if(this.browserEvent){
8167                 return E.getRelatedTarget(this.browserEvent);
8168             }
8169             return null;
8170         },
8171
8172         /**
8173          * Normalizes mouse wheel delta across browsers
8174          * @return {Number} The delta
8175          */
8176         getWheelDelta : function(){
8177             var e = this.browserEvent;
8178             var delta = 0;
8179             if(e.wheelDelta){ /* IE/Opera. */
8180                 delta = e.wheelDelta/120;
8181             }else if(e.detail){ /* Mozilla case. */
8182                 delta = -e.detail/3;
8183             }
8184             return delta;
8185         },
8186
8187         /**
8188          * Returns true if the control, meta, shift or alt key was pressed during this event.
8189          * @return {Boolean}
8190          */
8191         hasModifier : function(){
8192             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
8193         },
8194
8195         /**
8196          * Returns true if the target of this event equals el or is a child of el
8197          * @param {String/HTMLElement/Element} el
8198          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
8199          * @return {Boolean}
8200          */
8201         within : function(el, related){
8202             var t = this[related ? "getRelatedTarget" : "getTarget"]();
8203             return t && Roo.fly(el).contains(t);
8204         },
8205
8206         getPoint : function(){
8207             return new Roo.lib.Point(this.xy[0], this.xy[1]);
8208         }
8209     };
8210
8211     return new Roo.EventObjectImpl();
8212 }();
8213             
8214     /*
8215  * Based on:
8216  * Ext JS Library 1.1.1
8217  * Copyright(c) 2006-2007, Ext JS, LLC.
8218  *
8219  * Originally Released Under LGPL - original licence link has changed is not relivant.
8220  *
8221  * Fork - LGPL
8222  * <script type="text/javascript">
8223  */
8224
8225  
8226 // was in Composite Element!??!?!
8227  
8228 (function(){
8229     var D = Roo.lib.Dom;
8230     var E = Roo.lib.Event;
8231     var A = Roo.lib.Anim;
8232
8233     // local style camelizing for speed
8234     var propCache = {};
8235     var camelRe = /(-[a-z])/gi;
8236     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
8237     var view = document.defaultView;
8238
8239 /**
8240  * @class Roo.Element
8241  * Represents an Element in the DOM.<br><br>
8242  * Usage:<br>
8243 <pre><code>
8244 var el = Roo.get("my-div");
8245
8246 // or with getEl
8247 var el = getEl("my-div");
8248
8249 // or with a DOM element
8250 var el = Roo.get(myDivElement);
8251 </code></pre>
8252  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
8253  * each call instead of constructing a new one.<br><br>
8254  * <b>Animations</b><br />
8255  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
8256  * should either be a boolean (true) or an object literal with animation options. The animation options are:
8257 <pre>
8258 Option    Default   Description
8259 --------- --------  ---------------------------------------------
8260 duration  .35       The duration of the animation in seconds
8261 easing    easeOut   The YUI easing method
8262 callback  none      A function to execute when the anim completes
8263 scope     this      The scope (this) of the callback function
8264 </pre>
8265 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
8266 * manipulate the animation. Here's an example:
8267 <pre><code>
8268 var el = Roo.get("my-div");
8269
8270 // no animation
8271 el.setWidth(100);
8272
8273 // default animation
8274 el.setWidth(100, true);
8275
8276 // animation with some options set
8277 el.setWidth(100, {
8278     duration: 1,
8279     callback: this.foo,
8280     scope: this
8281 });
8282
8283 // using the "anim" property to get the Anim object
8284 var opt = {
8285     duration: 1,
8286     callback: this.foo,
8287     scope: this
8288 };
8289 el.setWidth(100, opt);
8290 ...
8291 if(opt.anim.isAnimated()){
8292     opt.anim.stop();
8293 }
8294 </code></pre>
8295 * <b> Composite (Collections of) Elements</b><br />
8296  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
8297  * @constructor Create a new Element directly.
8298  * @param {String/HTMLElement} element
8299  * @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).
8300  */
8301     Roo.Element = function(element, forceNew)
8302     {
8303         var dom = typeof element == "string" ?
8304                 document.getElementById(element) : element;
8305         
8306         this.listeners = {};
8307         
8308         if(!dom){ // invalid id/element
8309             return null;
8310         }
8311         var id = dom.id;
8312         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
8313             return Roo.Element.cache[id];
8314         }
8315
8316         /**
8317          * The DOM element
8318          * @type HTMLElement
8319          */
8320         this.dom = dom;
8321
8322         /**
8323          * The DOM element ID
8324          * @type String
8325          */
8326         this.id = id || Roo.id(dom);
8327         
8328         return this; // assumed for cctor?
8329     };
8330
8331     var El = Roo.Element;
8332
8333     El.prototype = {
8334         /**
8335          * The element's default display mode  (defaults to "") 
8336          * @type String
8337          */
8338         originalDisplay : "",
8339
8340         
8341         // note this is overridden in BS version..
8342         visibilityMode : 1, 
8343         /**
8344          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8345          * @type String
8346          */
8347         defaultUnit : "px",
8348         
8349         /**
8350          * Sets the element's visibility mode. When setVisible() is called it
8351          * will use this to determine whether to set the visibility or the display property.
8352          * @param visMode Element.VISIBILITY or Element.DISPLAY
8353          * @return {Roo.Element} this
8354          */
8355         setVisibilityMode : function(visMode){
8356             this.visibilityMode = visMode;
8357             return this;
8358         },
8359         /**
8360          * Convenience method for setVisibilityMode(Element.DISPLAY)
8361          * @param {String} display (optional) What to set display to when visible
8362          * @return {Roo.Element} this
8363          */
8364         enableDisplayMode : function(display){
8365             this.setVisibilityMode(El.DISPLAY);
8366             if(typeof display != "undefined") { this.originalDisplay = display; }
8367             return this;
8368         },
8369
8370         /**
8371          * 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)
8372          * @param {String} selector The simple selector to test
8373          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8374                 search as a number or element (defaults to 10 || document.body)
8375          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8376          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8377          */
8378         findParent : function(simpleSelector, maxDepth, returnEl){
8379             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8380             maxDepth = maxDepth || 50;
8381             if(typeof maxDepth != "number"){
8382                 stopEl = Roo.getDom(maxDepth);
8383                 maxDepth = 10;
8384             }
8385             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8386                 if(dq.is(p, simpleSelector)){
8387                     return returnEl ? Roo.get(p) : p;
8388                 }
8389                 depth++;
8390                 p = p.parentNode;
8391             }
8392             return null;
8393         },
8394
8395
8396         /**
8397          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8398          * @param {String} selector The simple selector to test
8399          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8400                 search as a number or element (defaults to 10 || document.body)
8401          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8402          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8403          */
8404         findParentNode : function(simpleSelector, maxDepth, returnEl){
8405             var p = Roo.fly(this.dom.parentNode, '_internal');
8406             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8407         },
8408         
8409         /**
8410          * Looks at  the scrollable parent element
8411          */
8412         findScrollableParent : function()
8413         {
8414             var overflowRegex = /(auto|scroll)/;
8415             
8416             if(this.getStyle('position') === 'fixed'){
8417                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8418             }
8419             
8420             var excludeStaticParent = this.getStyle('position') === "absolute";
8421             
8422             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8423                 
8424                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8425                     continue;
8426                 }
8427                 
8428                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8429                     return parent;
8430                 }
8431                 
8432                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8433                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8434                 }
8435             }
8436             
8437             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8438         },
8439
8440         /**
8441          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8442          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8443          * @param {String} selector The simple selector to test
8444          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8445                 search as a number or element (defaults to 10 || document.body)
8446          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8447          */
8448         up : function(simpleSelector, maxDepth){
8449             return this.findParentNode(simpleSelector, maxDepth, true);
8450         },
8451
8452
8453
8454         /**
8455          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8456          * @param {String} selector The simple selector to test
8457          * @return {Boolean} True if this element matches the selector, else false
8458          */
8459         is : function(simpleSelector){
8460             return Roo.DomQuery.is(this.dom, simpleSelector);
8461         },
8462
8463         /**
8464          * Perform animation on this element.
8465          * @param {Object} args The YUI animation control args
8466          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8467          * @param {Function} onComplete (optional) Function to call when animation completes
8468          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8469          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8470          * @return {Roo.Element} this
8471          */
8472         animate : function(args, duration, onComplete, easing, animType){
8473             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8474             return this;
8475         },
8476
8477         /*
8478          * @private Internal animation call
8479          */
8480         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8481             animType = animType || 'run';
8482             opt = opt || {};
8483             var anim = Roo.lib.Anim[animType](
8484                 this.dom, args,
8485                 (opt.duration || defaultDur) || .35,
8486                 (opt.easing || defaultEase) || 'easeOut',
8487                 function(){
8488                     Roo.callback(cb, this);
8489                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8490                 },
8491                 this
8492             );
8493             opt.anim = anim;
8494             return anim;
8495         },
8496
8497         // private legacy anim prep
8498         preanim : function(a, i){
8499             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8500         },
8501
8502         /**
8503          * Removes worthless text nodes
8504          * @param {Boolean} forceReclean (optional) By default the element
8505          * keeps track if it has been cleaned already so
8506          * you can call this over and over. However, if you update the element and
8507          * need to force a reclean, you can pass true.
8508          */
8509         clean : function(forceReclean){
8510             if(this.isCleaned && forceReclean !== true){
8511                 return this;
8512             }
8513             var ns = /\S/;
8514             var d = this.dom, n = d.firstChild, ni = -1;
8515             while(n){
8516                 var nx = n.nextSibling;
8517                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8518                     d.removeChild(n);
8519                 }else{
8520                     n.nodeIndex = ++ni;
8521                 }
8522                 n = nx;
8523             }
8524             this.isCleaned = true;
8525             return this;
8526         },
8527
8528         // private
8529         calcOffsetsTo : function(el){
8530             el = Roo.get(el);
8531             var d = el.dom;
8532             var restorePos = false;
8533             if(el.getStyle('position') == 'static'){
8534                 el.position('relative');
8535                 restorePos = true;
8536             }
8537             var x = 0, y =0;
8538             var op = this.dom;
8539             while(op && op != d && op.tagName != 'HTML'){
8540                 x+= op.offsetLeft;
8541                 y+= op.offsetTop;
8542                 op = op.offsetParent;
8543             }
8544             if(restorePos){
8545                 el.position('static');
8546             }
8547             return [x, y];
8548         },
8549
8550         /**
8551          * Scrolls this element into view within the passed container.
8552          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8553          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8554          * @return {Roo.Element} this
8555          */
8556         scrollIntoView : function(container, hscroll){
8557             var c = Roo.getDom(container) || document.body;
8558             var el = this.dom;
8559
8560             var o = this.calcOffsetsTo(c),
8561                 l = o[0],
8562                 t = o[1],
8563                 b = t+el.offsetHeight,
8564                 r = l+el.offsetWidth;
8565
8566             var ch = c.clientHeight;
8567             var ct = parseInt(c.scrollTop, 10);
8568             var cl = parseInt(c.scrollLeft, 10);
8569             var cb = ct + ch;
8570             var cr = cl + c.clientWidth;
8571
8572             if(t < ct){
8573                 c.scrollTop = t;
8574             }else if(b > cb){
8575                 c.scrollTop = b-ch;
8576             }
8577
8578             if(hscroll !== false){
8579                 if(l < cl){
8580                     c.scrollLeft = l;
8581                 }else if(r > cr){
8582                     c.scrollLeft = r-c.clientWidth;
8583                 }
8584             }
8585             return this;
8586         },
8587
8588         // private
8589         scrollChildIntoView : function(child, hscroll){
8590             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8591         },
8592
8593         /**
8594          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8595          * the new height may not be available immediately.
8596          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8597          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8598          * @param {Function} onComplete (optional) Function to call when animation completes
8599          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8600          * @return {Roo.Element} this
8601          */
8602         autoHeight : function(animate, duration, onComplete, easing){
8603             var oldHeight = this.getHeight();
8604             this.clip();
8605             this.setHeight(1); // force clipping
8606             setTimeout(function(){
8607                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8608                 if(!animate){
8609                     this.setHeight(height);
8610                     this.unclip();
8611                     if(typeof onComplete == "function"){
8612                         onComplete();
8613                     }
8614                 }else{
8615                     this.setHeight(oldHeight); // restore original height
8616                     this.setHeight(height, animate, duration, function(){
8617                         this.unclip();
8618                         if(typeof onComplete == "function") { onComplete(); }
8619                     }.createDelegate(this), easing);
8620                 }
8621             }.createDelegate(this), 0);
8622             return this;
8623         },
8624
8625         /**
8626          * Returns true if this element is an ancestor of the passed element
8627          * @param {HTMLElement/String} el The element to check
8628          * @return {Boolean} True if this element is an ancestor of el, else false
8629          */
8630         contains : function(el){
8631             if(!el){return false;}
8632             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8633         },
8634
8635         /**
8636          * Checks whether the element is currently visible using both visibility and display properties.
8637          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8638          * @return {Boolean} True if the element is currently visible, else false
8639          */
8640         isVisible : function(deep) {
8641             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8642             if(deep !== true || !vis){
8643                 return vis;
8644             }
8645             var p = this.dom.parentNode;
8646             while(p && p.tagName.toLowerCase() != "body"){
8647                 if(!Roo.fly(p, '_isVisible').isVisible()){
8648                     return false;
8649                 }
8650                 p = p.parentNode;
8651             }
8652             return true;
8653         },
8654
8655         /**
8656          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8657          * @param {String} selector The CSS selector
8658          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8659          * @return {CompositeElement/CompositeElementLite} The composite element
8660          */
8661         select : function(selector, unique){
8662             return El.select(selector, unique, this.dom);
8663         },
8664
8665         /**
8666          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8667          * @param {String} selector The CSS selector
8668          * @return {Array} An array of the matched nodes
8669          */
8670         query : function(selector, unique){
8671             return Roo.DomQuery.select(selector, this.dom);
8672         },
8673
8674         /**
8675          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8676          * @param {String} selector The CSS selector
8677          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8678          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8679          */
8680         child : function(selector, returnDom){
8681             var n = Roo.DomQuery.selectNode(selector, this.dom);
8682             return returnDom ? n : Roo.get(n);
8683         },
8684
8685         /**
8686          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8687          * @param {String} selector The CSS selector
8688          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8689          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8690          */
8691         down : function(selector, returnDom){
8692             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8693             return returnDom ? n : Roo.get(n);
8694         },
8695
8696         /**
8697          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8698          * @param {String} group The group the DD object is member of
8699          * @param {Object} config The DD config object
8700          * @param {Object} overrides An object containing methods to override/implement on the DD object
8701          * @return {Roo.dd.DD} The DD object
8702          */
8703         initDD : function(group, config, overrides){
8704             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8705             return Roo.apply(dd, overrides);
8706         },
8707
8708         /**
8709          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8710          * @param {String} group The group the DDProxy object is member of
8711          * @param {Object} config The DDProxy config object
8712          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8713          * @return {Roo.dd.DDProxy} The DDProxy object
8714          */
8715         initDDProxy : function(group, config, overrides){
8716             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8717             return Roo.apply(dd, overrides);
8718         },
8719
8720         /**
8721          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8722          * @param {String} group The group the DDTarget object is member of
8723          * @param {Object} config The DDTarget config object
8724          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8725          * @return {Roo.dd.DDTarget} The DDTarget object
8726          */
8727         initDDTarget : function(group, config, overrides){
8728             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8729             return Roo.apply(dd, overrides);
8730         },
8731
8732         /**
8733          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8734          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8735          * @param {Boolean} visible Whether the element is visible
8736          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8737          * @return {Roo.Element} this
8738          */
8739          setVisible : function(visible, animate){
8740             if(!animate || !A){
8741                 if(this.visibilityMode == El.DISPLAY){
8742                     this.setDisplayed(visible);
8743                 }else{
8744                     this.fixDisplay();
8745                     this.dom.style.visibility = visible ? "visible" : "hidden";
8746                 }
8747             }else{
8748                 // closure for composites
8749                 var dom = this.dom;
8750                 var visMode = this.visibilityMode;
8751                 if(visible){
8752                     this.setOpacity(.01);
8753                     this.setVisible(true);
8754                 }
8755                 this.anim({opacity: { to: (visible?1:0) }},
8756                       this.preanim(arguments, 1),
8757                       null, .35, 'easeIn', function(){
8758                          if(!visible){
8759                              if(visMode == El.DISPLAY){
8760                                  dom.style.display = "none";
8761                              }else{
8762                                  dom.style.visibility = "hidden";
8763                              }
8764                              Roo.get(dom).setOpacity(1);
8765                          }
8766                      });
8767             }
8768             return this;
8769         },
8770
8771         /**
8772          * Returns true if display is not "none"
8773          * @return {Boolean}
8774          */
8775         isDisplayed : function() {
8776             return this.getStyle("display") != "none";
8777         },
8778
8779         /**
8780          * Toggles the element's visibility or display, depending on visibility mode.
8781          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8782          * @return {Roo.Element} this
8783          */
8784         toggle : function(animate){
8785             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8786             return this;
8787         },
8788
8789         /**
8790          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8791          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8792          * @return {Roo.Element} this
8793          */
8794         setDisplayed : function(value) {
8795             if(typeof value == "boolean"){
8796                value = value ? this.originalDisplay : "none";
8797             }
8798             this.setStyle("display", value);
8799             return this;
8800         },
8801
8802         /**
8803          * Tries to focus the element. Any exceptions are caught and ignored.
8804          * @return {Roo.Element} this
8805          */
8806         focus : function() {
8807             try{
8808                 this.dom.focus();
8809             }catch(e){}
8810             return this;
8811         },
8812
8813         /**
8814          * Tries to blur the element. Any exceptions are caught and ignored.
8815          * @return {Roo.Element} this
8816          */
8817         blur : function() {
8818             try{
8819                 this.dom.blur();
8820             }catch(e){}
8821             return this;
8822         },
8823
8824         /**
8825          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8826          * @param {String/Array} className The CSS class to add, or an array of classes
8827          * @return {Roo.Element} this
8828          */
8829         addClass : function(className){
8830             if(className instanceof Array){
8831                 for(var i = 0, len = className.length; i < len; i++) {
8832                     this.addClass(className[i]);
8833                 }
8834             }else{
8835                 if(className && !this.hasClass(className)){
8836                     if (this.dom instanceof SVGElement) {
8837                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
8838                     } else {
8839                         this.dom.className = this.dom.className + " " + className;
8840                     }
8841                 }
8842             }
8843             return this;
8844         },
8845
8846         /**
8847          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8848          * @param {String/Array} className The CSS class to add, or an array of classes
8849          * @return {Roo.Element} this
8850          */
8851         radioClass : function(className){
8852             var siblings = this.dom.parentNode.childNodes;
8853             for(var i = 0; i < siblings.length; i++) {
8854                 var s = siblings[i];
8855                 if(s.nodeType == 1){
8856                     Roo.get(s).removeClass(className);
8857                 }
8858             }
8859             this.addClass(className);
8860             return this;
8861         },
8862
8863         /**
8864          * Removes one or more CSS classes from the element.
8865          * @param {String/Array} className The CSS class to remove, or an array of classes
8866          * @return {Roo.Element} this
8867          */
8868         removeClass : function(className){
8869             
8870             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8871             if(!className || !cn){
8872                 return this;
8873             }
8874             if(className instanceof Array){
8875                 for(var i = 0, len = className.length; i < len; i++) {
8876                     this.removeClass(className[i]);
8877                 }
8878             }else{
8879                 if(this.hasClass(className)){
8880                     var re = this.classReCache[className];
8881                     if (!re) {
8882                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8883                        this.classReCache[className] = re;
8884                     }
8885                     if (this.dom instanceof SVGElement) {
8886                         this.dom.className.baseVal = cn.replace(re, " ");
8887                     } else {
8888                         this.dom.className = cn.replace(re, " ");
8889                     }
8890                 }
8891             }
8892             return this;
8893         },
8894
8895         // private
8896         classReCache: {},
8897
8898         /**
8899          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8900          * @param {String} className The CSS class to toggle
8901          * @return {Roo.Element} this
8902          */
8903         toggleClass : function(className){
8904             if(this.hasClass(className)){
8905                 this.removeClass(className);
8906             }else{
8907                 this.addClass(className);
8908             }
8909             return this;
8910         },
8911
8912         /**
8913          * Checks if the specified CSS class exists on this element's DOM node.
8914          * @param {String} className The CSS class to check for
8915          * @return {Boolean} True if the class exists, else false
8916          */
8917         hasClass : function(className){
8918             if (this.dom instanceof SVGElement) {
8919                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
8920             } 
8921             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8922         },
8923
8924         /**
8925          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
8926          * @param {String} oldClassName The CSS class to replace
8927          * @param {String} newClassName The replacement CSS class
8928          * @return {Roo.Element} this
8929          */
8930         replaceClass : function(oldClassName, newClassName){
8931             this.removeClass(oldClassName);
8932             this.addClass(newClassName);
8933             return this;
8934         },
8935
8936         /**
8937          * Returns an object with properties matching the styles requested.
8938          * For example, el.getStyles('color', 'font-size', 'width') might return
8939          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8940          * @param {String} style1 A style name
8941          * @param {String} style2 A style name
8942          * @param {String} etc.
8943          * @return {Object} The style object
8944          */
8945         getStyles : function(){
8946             var a = arguments, len = a.length, r = {};
8947             for(var i = 0; i < len; i++){
8948                 r[a[i]] = this.getStyle(a[i]);
8949             }
8950             return r;
8951         },
8952
8953         /**
8954          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8955          * @param {String} property The style property whose value is returned.
8956          * @return {String} The current value of the style property for this element.
8957          */
8958         getStyle : function(){
8959             return view && view.getComputedStyle ?
8960                 function(prop){
8961                     var el = this.dom, v, cs, camel;
8962                     if(prop == 'float'){
8963                         prop = "cssFloat";
8964                     }
8965                     if(el.style && (v = el.style[prop])){
8966                         return v;
8967                     }
8968                     if(cs = view.getComputedStyle(el, "")){
8969                         if(!(camel = propCache[prop])){
8970                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
8971                         }
8972                         return cs[camel];
8973                     }
8974                     return null;
8975                 } :
8976                 function(prop){
8977                     var el = this.dom, v, cs, camel;
8978                     if(prop == 'opacity'){
8979                         if(typeof el.style.filter == 'string'){
8980                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8981                             if(m){
8982                                 var fv = parseFloat(m[1]);
8983                                 if(!isNaN(fv)){
8984                                     return fv ? fv / 100 : 0;
8985                                 }
8986                             }
8987                         }
8988                         return 1;
8989                     }else if(prop == 'float'){
8990                         prop = "styleFloat";
8991                     }
8992                     if(!(camel = propCache[prop])){
8993                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
8994                     }
8995                     if(v = el.style[camel]){
8996                         return v;
8997                     }
8998                     if(cs = el.currentStyle){
8999                         return cs[camel];
9000                     }
9001                     return null;
9002                 };
9003         }(),
9004
9005         /**
9006          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
9007          * @param {String/Object} property The style property to be set, or an object of multiple styles.
9008          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
9009          * @return {Roo.Element} this
9010          */
9011         setStyle : function(prop, value){
9012             if(typeof prop == "string"){
9013                 
9014                 if (prop == 'float') {
9015                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
9016                     return this;
9017                 }
9018                 
9019                 var camel;
9020                 if(!(camel = propCache[prop])){
9021                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
9022                 }
9023                 
9024                 if(camel == 'opacity') {
9025                     this.setOpacity(value);
9026                 }else{
9027                     this.dom.style[camel] = value;
9028                 }
9029             }else{
9030                 for(var style in prop){
9031                     if(typeof prop[style] != "function"){
9032                        this.setStyle(style, prop[style]);
9033                     }
9034                 }
9035             }
9036             return this;
9037         },
9038
9039         /**
9040          * More flexible version of {@link #setStyle} for setting style properties.
9041          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
9042          * a function which returns such a specification.
9043          * @return {Roo.Element} this
9044          */
9045         applyStyles : function(style){
9046             Roo.DomHelper.applyStyles(this.dom, style);
9047             return this;
9048         },
9049
9050         /**
9051           * 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).
9052           * @return {Number} The X position of the element
9053           */
9054         getX : function(){
9055             return D.getX(this.dom);
9056         },
9057
9058         /**
9059           * 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).
9060           * @return {Number} The Y position of the element
9061           */
9062         getY : function(){
9063             return D.getY(this.dom);
9064         },
9065
9066         /**
9067           * 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).
9068           * @return {Array} The XY position of the element
9069           */
9070         getXY : function(){
9071             return D.getXY(this.dom);
9072         },
9073
9074         /**
9075          * 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).
9076          * @param {Number} The X position of the element
9077          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9078          * @return {Roo.Element} this
9079          */
9080         setX : function(x, animate){
9081             if(!animate || !A){
9082                 D.setX(this.dom, x);
9083             }else{
9084                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
9085             }
9086             return this;
9087         },
9088
9089         /**
9090          * 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).
9091          * @param {Number} The Y position of the element
9092          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9093          * @return {Roo.Element} this
9094          */
9095         setY : function(y, animate){
9096             if(!animate || !A){
9097                 D.setY(this.dom, y);
9098             }else{
9099                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
9100             }
9101             return this;
9102         },
9103
9104         /**
9105          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
9106          * @param {String} left The left CSS property value
9107          * @return {Roo.Element} this
9108          */
9109         setLeft : function(left){
9110             this.setStyle("left", this.addUnits(left));
9111             return this;
9112         },
9113
9114         /**
9115          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
9116          * @param {String} top The top CSS property value
9117          * @return {Roo.Element} this
9118          */
9119         setTop : function(top){
9120             this.setStyle("top", this.addUnits(top));
9121             return this;
9122         },
9123
9124         /**
9125          * Sets the element's CSS right style.
9126          * @param {String} right The right CSS property value
9127          * @return {Roo.Element} this
9128          */
9129         setRight : function(right){
9130             this.setStyle("right", this.addUnits(right));
9131             return this;
9132         },
9133
9134         /**
9135          * Sets the element's CSS bottom style.
9136          * @param {String} bottom The bottom CSS property value
9137          * @return {Roo.Element} this
9138          */
9139         setBottom : function(bottom){
9140             this.setStyle("bottom", this.addUnits(bottom));
9141             return this;
9142         },
9143
9144         /**
9145          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9146          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9147          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
9148          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9149          * @return {Roo.Element} this
9150          */
9151         setXY : function(pos, animate){
9152             if(!animate || !A){
9153                 D.setXY(this.dom, pos);
9154             }else{
9155                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
9156             }
9157             return this;
9158         },
9159
9160         /**
9161          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9162          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9163          * @param {Number} x X value for new position (coordinates are page-based)
9164          * @param {Number} y Y value for new position (coordinates are page-based)
9165          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9166          * @return {Roo.Element} this
9167          */
9168         setLocation : function(x, y, animate){
9169             this.setXY([x, y], this.preanim(arguments, 2));
9170             return this;
9171         },
9172
9173         /**
9174          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9175          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9176          * @param {Number} x X value for new position (coordinates are page-based)
9177          * @param {Number} y Y value for new position (coordinates are page-based)
9178          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9179          * @return {Roo.Element} this
9180          */
9181         moveTo : function(x, y, animate){
9182             this.setXY([x, y], this.preanim(arguments, 2));
9183             return this;
9184         },
9185
9186         /**
9187          * Returns the region of the given element.
9188          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
9189          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
9190          */
9191         getRegion : function(){
9192             return D.getRegion(this.dom);
9193         },
9194
9195         /**
9196          * Returns the offset height of the element
9197          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
9198          * @return {Number} The element's height
9199          */
9200         getHeight : function(contentHeight){
9201             var h = this.dom.offsetHeight || 0;
9202             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
9203         },
9204
9205         /**
9206          * Returns the offset width of the element
9207          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
9208          * @return {Number} The element's width
9209          */
9210         getWidth : function(contentWidth){
9211             var w = this.dom.offsetWidth || 0;
9212             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
9213         },
9214
9215         /**
9216          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
9217          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
9218          * if a height has not been set using CSS.
9219          * @return {Number}
9220          */
9221         getComputedHeight : function(){
9222             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
9223             if(!h){
9224                 h = parseInt(this.getStyle('height'), 10) || 0;
9225                 if(!this.isBorderBox()){
9226                     h += this.getFrameWidth('tb');
9227                 }
9228             }
9229             return h;
9230         },
9231
9232         /**
9233          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
9234          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
9235          * if a width has not been set using CSS.
9236          * @return {Number}
9237          */
9238         getComputedWidth : function(){
9239             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
9240             if(!w){
9241                 w = parseInt(this.getStyle('width'), 10) || 0;
9242                 if(!this.isBorderBox()){
9243                     w += this.getFrameWidth('lr');
9244                 }
9245             }
9246             return w;
9247         },
9248
9249         /**
9250          * Returns the size of the element.
9251          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
9252          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
9253          */
9254         getSize : function(contentSize){
9255             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
9256         },
9257
9258         /**
9259          * Returns the width and height of the viewport.
9260          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
9261          */
9262         getViewSize : function(){
9263             var d = this.dom, doc = document, aw = 0, ah = 0;
9264             if(d == doc || d == doc.body){
9265                 return {width : D.getViewWidth(), height: D.getViewHeight()};
9266             }else{
9267                 return {
9268                     width : d.clientWidth,
9269                     height: d.clientHeight
9270                 };
9271             }
9272         },
9273
9274         /**
9275          * Returns the value of the "value" attribute
9276          * @param {Boolean} asNumber true to parse the value as a number
9277          * @return {String/Number}
9278          */
9279         getValue : function(asNumber){
9280             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
9281         },
9282
9283         // private
9284         adjustWidth : function(width){
9285             if(typeof width == "number"){
9286                 if(this.autoBoxAdjust && !this.isBorderBox()){
9287                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9288                 }
9289                 if(width < 0){
9290                     width = 0;
9291                 }
9292             }
9293             return width;
9294         },
9295
9296         // private
9297         adjustHeight : function(height){
9298             if(typeof height == "number"){
9299                if(this.autoBoxAdjust && !this.isBorderBox()){
9300                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9301                }
9302                if(height < 0){
9303                    height = 0;
9304                }
9305             }
9306             return height;
9307         },
9308
9309         /**
9310          * Set the width of the element
9311          * @param {Number} width The new width
9312          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9313          * @return {Roo.Element} this
9314          */
9315         setWidth : function(width, animate){
9316             width = this.adjustWidth(width);
9317             if(!animate || !A){
9318                 this.dom.style.width = this.addUnits(width);
9319             }else{
9320                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9321             }
9322             return this;
9323         },
9324
9325         /**
9326          * Set the height of the element
9327          * @param {Number} height The new height
9328          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9329          * @return {Roo.Element} this
9330          */
9331          setHeight : function(height, animate){
9332             height = this.adjustHeight(height);
9333             if(!animate || !A){
9334                 this.dom.style.height = this.addUnits(height);
9335             }else{
9336                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9337             }
9338             return this;
9339         },
9340
9341         /**
9342          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9343          * @param {Number} width The new width
9344          * @param {Number} height The new height
9345          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9346          * @return {Roo.Element} this
9347          */
9348          setSize : function(width, height, animate){
9349             if(typeof width == "object"){ // in case of object from getSize()
9350                 height = width.height; width = width.width;
9351             }
9352             width = this.adjustWidth(width); height = this.adjustHeight(height);
9353             if(!animate || !A){
9354                 this.dom.style.width = this.addUnits(width);
9355                 this.dom.style.height = this.addUnits(height);
9356             }else{
9357                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9358             }
9359             return this;
9360         },
9361
9362         /**
9363          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9364          * @param {Number} x X value for new position (coordinates are page-based)
9365          * @param {Number} y Y value for new position (coordinates are page-based)
9366          * @param {Number} width The new width
9367          * @param {Number} height The new height
9368          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9369          * @return {Roo.Element} this
9370          */
9371         setBounds : function(x, y, width, height, animate){
9372             if(!animate || !A){
9373                 this.setSize(width, height);
9374                 this.setLocation(x, y);
9375             }else{
9376                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9377                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9378                               this.preanim(arguments, 4), 'motion');
9379             }
9380             return this;
9381         },
9382
9383         /**
9384          * 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.
9385          * @param {Roo.lib.Region} region The region to fill
9386          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9387          * @return {Roo.Element} this
9388          */
9389         setRegion : function(region, animate){
9390             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9391             return this;
9392         },
9393
9394         /**
9395          * Appends an event handler
9396          *
9397          * @param {String}   eventName     The type of event to append
9398          * @param {Function} fn        The method the event invokes
9399          * @param {Object} scope       (optional) The scope (this object) of the fn
9400          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9401          */
9402         addListener : function(eventName, fn, scope, options)
9403         {
9404             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9405                 this.addListener('touchstart', this.onTapHandler, this);
9406             }
9407             
9408             // we need to handle a special case where dom element is a svg element.
9409             // in this case we do not actua
9410             if (!this.dom) {
9411                 return;
9412             }
9413             
9414             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9415                 if (typeof(this.listeners[eventName]) == 'undefined') {
9416                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9417                 }
9418                 this.listeners[eventName].addListener(fn, scope, options);
9419                 return;
9420             }
9421             
9422                 
9423             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9424             
9425             
9426         },
9427         tapedTwice : false,
9428         onTapHandler : function(event)
9429         {
9430             if(!this.tapedTwice) {
9431                 this.tapedTwice = true;
9432                 var s = this;
9433                 setTimeout( function() {
9434                     s.tapedTwice = false;
9435                 }, 300 );
9436                 return;
9437             }
9438             event.preventDefault();
9439             var revent = new MouseEvent('dblclick',  {
9440                 view: window,
9441                 bubbles: true,
9442                 cancelable: true
9443             });
9444              
9445             this.dom.dispatchEvent(revent);
9446             //action on double tap goes below
9447              
9448         }, 
9449  
9450         /**
9451          * Removes an event handler from this element
9452          * @param {String} eventName the type of event to remove
9453          * @param {Function} fn the method the event invokes
9454          * @param {Function} scope (needed for svg fake listeners)
9455          * @return {Roo.Element} this
9456          */
9457         removeListener : function(eventName, fn, scope){
9458             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9459             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9460                 return this;
9461             }
9462             this.listeners[eventName].removeListener(fn, scope);
9463             return this;
9464         },
9465
9466         /**
9467          * Removes all previous added listeners from this element
9468          * @return {Roo.Element} this
9469          */
9470         removeAllListeners : function(){
9471             E.purgeElement(this.dom);
9472             this.listeners = {};
9473             return this;
9474         },
9475
9476         relayEvent : function(eventName, observable){
9477             this.on(eventName, function(e){
9478                 observable.fireEvent(eventName, e);
9479             });
9480         },
9481
9482         
9483         /**
9484          * Set the opacity of the element
9485          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9486          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9487          * @return {Roo.Element} this
9488          */
9489          setOpacity : function(opacity, animate){
9490             if(!animate || !A){
9491                 var s = this.dom.style;
9492                 if(Roo.isIE){
9493                     s.zoom = 1;
9494                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9495                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9496                 }else{
9497                     s.opacity = opacity;
9498                 }
9499             }else{
9500                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9501             }
9502             return this;
9503         },
9504
9505         /**
9506          * Gets the left X coordinate
9507          * @param {Boolean} local True to get the local css position instead of page coordinate
9508          * @return {Number}
9509          */
9510         getLeft : function(local){
9511             if(!local){
9512                 return this.getX();
9513             }else{
9514                 return parseInt(this.getStyle("left"), 10) || 0;
9515             }
9516         },
9517
9518         /**
9519          * Gets the right X coordinate of the element (element X position + element width)
9520          * @param {Boolean} local True to get the local css position instead of page coordinate
9521          * @return {Number}
9522          */
9523         getRight : function(local){
9524             if(!local){
9525                 return this.getX() + this.getWidth();
9526             }else{
9527                 return (this.getLeft(true) + this.getWidth()) || 0;
9528             }
9529         },
9530
9531         /**
9532          * Gets the top Y coordinate
9533          * @param {Boolean} local True to get the local css position instead of page coordinate
9534          * @return {Number}
9535          */
9536         getTop : function(local) {
9537             if(!local){
9538                 return this.getY();
9539             }else{
9540                 return parseInt(this.getStyle("top"), 10) || 0;
9541             }
9542         },
9543
9544         /**
9545          * Gets the bottom Y coordinate of the element (element Y position + element height)
9546          * @param {Boolean} local True to get the local css position instead of page coordinate
9547          * @return {Number}
9548          */
9549         getBottom : function(local){
9550             if(!local){
9551                 return this.getY() + this.getHeight();
9552             }else{
9553                 return (this.getTop(true) + this.getHeight()) || 0;
9554             }
9555         },
9556
9557         /**
9558         * Initializes positioning on this element. If a desired position is not passed, it will make the
9559         * the element positioned relative IF it is not already positioned.
9560         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9561         * @param {Number} zIndex (optional) The zIndex to apply
9562         * @param {Number} x (optional) Set the page X position
9563         * @param {Number} y (optional) Set the page Y position
9564         */
9565         position : function(pos, zIndex, x, y){
9566             if(!pos){
9567                if(this.getStyle('position') == 'static'){
9568                    this.setStyle('position', 'relative');
9569                }
9570             }else{
9571                 this.setStyle("position", pos);
9572             }
9573             if(zIndex){
9574                 this.setStyle("z-index", zIndex);
9575             }
9576             if(x !== undefined && y !== undefined){
9577                 this.setXY([x, y]);
9578             }else if(x !== undefined){
9579                 this.setX(x);
9580             }else if(y !== undefined){
9581                 this.setY(y);
9582             }
9583         },
9584
9585         /**
9586         * Clear positioning back to the default when the document was loaded
9587         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9588         * @return {Roo.Element} this
9589          */
9590         clearPositioning : function(value){
9591             value = value ||'';
9592             this.setStyle({
9593                 "left": value,
9594                 "right": value,
9595                 "top": value,
9596                 "bottom": value,
9597                 "z-index": "",
9598                 "position" : "static"
9599             });
9600             return this;
9601         },
9602
9603         /**
9604         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9605         * snapshot before performing an update and then restoring the element.
9606         * @return {Object}
9607         */
9608         getPositioning : function(){
9609             var l = this.getStyle("left");
9610             var t = this.getStyle("top");
9611             return {
9612                 "position" : this.getStyle("position"),
9613                 "left" : l,
9614                 "right" : l ? "" : this.getStyle("right"),
9615                 "top" : t,
9616                 "bottom" : t ? "" : this.getStyle("bottom"),
9617                 "z-index" : this.getStyle("z-index")
9618             };
9619         },
9620
9621         /**
9622          * Gets the width of the border(s) for the specified side(s)
9623          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9624          * passing lr would get the border (l)eft width + the border (r)ight width.
9625          * @return {Number} The width of the sides passed added together
9626          */
9627         getBorderWidth : function(side){
9628             return this.addStyles(side, El.borders);
9629         },
9630
9631         /**
9632          * Gets the width of the padding(s) for the specified side(s)
9633          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9634          * passing lr would get the padding (l)eft + the padding (r)ight.
9635          * @return {Number} The padding of the sides passed added together
9636          */
9637         getPadding : function(side){
9638             return this.addStyles(side, El.paddings);
9639         },
9640
9641         /**
9642         * Set positioning with an object returned by getPositioning().
9643         * @param {Object} posCfg
9644         * @return {Roo.Element} this
9645          */
9646         setPositioning : function(pc){
9647             this.applyStyles(pc);
9648             if(pc.right == "auto"){
9649                 this.dom.style.right = "";
9650             }
9651             if(pc.bottom == "auto"){
9652                 this.dom.style.bottom = "";
9653             }
9654             return this;
9655         },
9656
9657         // private
9658         fixDisplay : function(){
9659             if(this.getStyle("display") == "none"){
9660                 this.setStyle("visibility", "hidden");
9661                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9662                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9663                     this.setStyle("display", "block");
9664                 }
9665             }
9666         },
9667
9668         /**
9669          * Quick set left and top adding default units
9670          * @param {String} left The left CSS property value
9671          * @param {String} top The top CSS property value
9672          * @return {Roo.Element} this
9673          */
9674          setLeftTop : function(left, top){
9675             this.dom.style.left = this.addUnits(left);
9676             this.dom.style.top = this.addUnits(top);
9677             return this;
9678         },
9679
9680         /**
9681          * Move this element relative to its current position.
9682          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9683          * @param {Number} distance How far to move the element in pixels
9684          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9685          * @return {Roo.Element} this
9686          */
9687          move : function(direction, distance, animate){
9688             var xy = this.getXY();
9689             direction = direction.toLowerCase();
9690             switch(direction){
9691                 case "l":
9692                 case "left":
9693                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9694                     break;
9695                case "r":
9696                case "right":
9697                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9698                     break;
9699                case "t":
9700                case "top":
9701                case "up":
9702                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9703                     break;
9704                case "b":
9705                case "bottom":
9706                case "down":
9707                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9708                     break;
9709             }
9710             return this;
9711         },
9712
9713         /**
9714          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9715          * @return {Roo.Element} this
9716          */
9717         clip : function(){
9718             if(!this.isClipped){
9719                this.isClipped = true;
9720                this.originalClip = {
9721                    "o": this.getStyle("overflow"),
9722                    "x": this.getStyle("overflow-x"),
9723                    "y": this.getStyle("overflow-y")
9724                };
9725                this.setStyle("overflow", "hidden");
9726                this.setStyle("overflow-x", "hidden");
9727                this.setStyle("overflow-y", "hidden");
9728             }
9729             return this;
9730         },
9731
9732         /**
9733          *  Return clipping (overflow) to original clipping before clip() was called
9734          * @return {Roo.Element} this
9735          */
9736         unclip : function(){
9737             if(this.isClipped){
9738                 this.isClipped = false;
9739                 var o = this.originalClip;
9740                 if(o.o){this.setStyle("overflow", o.o);}
9741                 if(o.x){this.setStyle("overflow-x", o.x);}
9742                 if(o.y){this.setStyle("overflow-y", o.y);}
9743             }
9744             return this;
9745         },
9746
9747
9748         /**
9749          * Gets the x,y coordinates specified by the anchor position on the element.
9750          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9751          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9752          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9753          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9754          * @return {Array} [x, y] An array containing the element's x and y coordinates
9755          */
9756         getAnchorXY : function(anchor, local, s){
9757             //Passing a different size is useful for pre-calculating anchors,
9758             //especially for anchored animations that change the el size.
9759
9760             var w, h, vp = false;
9761             if(!s){
9762                 var d = this.dom;
9763                 if(d == document.body || d == document){
9764                     vp = true;
9765                     w = D.getViewWidth(); h = D.getViewHeight();
9766                 }else{
9767                     w = this.getWidth(); h = this.getHeight();
9768                 }
9769             }else{
9770                 w = s.width;  h = s.height;
9771             }
9772             var x = 0, y = 0, r = Math.round;
9773             switch((anchor || "tl").toLowerCase()){
9774                 case "c":
9775                     x = r(w*.5);
9776                     y = r(h*.5);
9777                 break;
9778                 case "t":
9779                     x = r(w*.5);
9780                     y = 0;
9781                 break;
9782                 case "l":
9783                     x = 0;
9784                     y = r(h*.5);
9785                 break;
9786                 case "r":
9787                     x = w;
9788                     y = r(h*.5);
9789                 break;
9790                 case "b":
9791                     x = r(w*.5);
9792                     y = h;
9793                 break;
9794                 case "tl":
9795                     x = 0;
9796                     y = 0;
9797                 break;
9798                 case "bl":
9799                     x = 0;
9800                     y = h;
9801                 break;
9802                 case "br":
9803                     x = w;
9804                     y = h;
9805                 break;
9806                 case "tr":
9807                     x = w;
9808                     y = 0;
9809                 break;
9810             }
9811             if(local === true){
9812                 return [x, y];
9813             }
9814             if(vp){
9815                 var sc = this.getScroll();
9816                 return [x + sc.left, y + sc.top];
9817             }
9818             //Add the element's offset xy
9819             var o = this.getXY();
9820             return [x+o[0], y+o[1]];
9821         },
9822
9823         /**
9824          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9825          * supported position values.
9826          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9827          * @param {String} position The position to align to.
9828          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9829          * @return {Array} [x, y]
9830          */
9831         getAlignToXY : function(el, p, o)
9832         {
9833             el = Roo.get(el);
9834             var d = this.dom;
9835             if(!el.dom){
9836                 throw "Element.alignTo with an element that doesn't exist";
9837             }
9838             var c = false; //constrain to viewport
9839             var p1 = "", p2 = "";
9840             o = o || [0,0];
9841
9842             if(!p){
9843                 p = "tl-bl";
9844             }else if(p == "?"){
9845                 p = "tl-bl?";
9846             }else if(p.indexOf("-") == -1){
9847                 p = "tl-" + p;
9848             }
9849             p = p.toLowerCase();
9850             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9851             if(!m){
9852                throw "Element.alignTo with an invalid alignment " + p;
9853             }
9854             p1 = m[1]; p2 = m[2]; c = !!m[3];
9855
9856             //Subtract the aligned el's internal xy from the target's offset xy
9857             //plus custom offset to get the aligned el's new offset xy
9858             var a1 = this.getAnchorXY(p1, true);
9859             var a2 = el.getAnchorXY(p2, false);
9860             var x = a2[0] - a1[0] + o[0];
9861             var y = a2[1] - a1[1] + o[1];
9862             if(c){
9863                 //constrain the aligned el to viewport if necessary
9864                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9865                 // 5px of margin for ie
9866                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9867
9868                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9869                 //perpendicular to the vp border, allow the aligned el to slide on that border,
9870                 //otherwise swap the aligned el to the opposite border of the target.
9871                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9872                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9873                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
9874                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9875
9876                var doc = document;
9877                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9878                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9879
9880                if((x+w) > dw + scrollX){
9881                     x = swapX ? r.left-w : dw+scrollX-w;
9882                 }
9883                if(x < scrollX){
9884                    x = swapX ? r.right : scrollX;
9885                }
9886                if((y+h) > dh + scrollY){
9887                     y = swapY ? r.top-h : dh+scrollY-h;
9888                 }
9889                if (y < scrollY){
9890                    y = swapY ? r.bottom : scrollY;
9891                }
9892             }
9893             return [x,y];
9894         },
9895
9896         // private
9897         getConstrainToXY : function(){
9898             var os = {top:0, left:0, bottom:0, right: 0};
9899
9900             return function(el, local, offsets, proposedXY){
9901                 el = Roo.get(el);
9902                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9903
9904                 var vw, vh, vx = 0, vy = 0;
9905                 if(el.dom == document.body || el.dom == document){
9906                     vw = Roo.lib.Dom.getViewWidth();
9907                     vh = Roo.lib.Dom.getViewHeight();
9908                 }else{
9909                     vw = el.dom.clientWidth;
9910                     vh = el.dom.clientHeight;
9911                     if(!local){
9912                         var vxy = el.getXY();
9913                         vx = vxy[0];
9914                         vy = vxy[1];
9915                     }
9916                 }
9917
9918                 var s = el.getScroll();
9919
9920                 vx += offsets.left + s.left;
9921                 vy += offsets.top + s.top;
9922
9923                 vw -= offsets.right;
9924                 vh -= offsets.bottom;
9925
9926                 var vr = vx+vw;
9927                 var vb = vy+vh;
9928
9929                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9930                 var x = xy[0], y = xy[1];
9931                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9932
9933                 // only move it if it needs it
9934                 var moved = false;
9935
9936                 // first validate right/bottom
9937                 if((x + w) > vr){
9938                     x = vr - w;
9939                     moved = true;
9940                 }
9941                 if((y + h) > vb){
9942                     y = vb - h;
9943                     moved = true;
9944                 }
9945                 // then make sure top/left isn't negative
9946                 if(x < vx){
9947                     x = vx;
9948                     moved = true;
9949                 }
9950                 if(y < vy){
9951                     y = vy;
9952                     moved = true;
9953                 }
9954                 return moved ? [x, y] : false;
9955             };
9956         }(),
9957
9958         // private
9959         adjustForConstraints : function(xy, parent, offsets){
9960             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
9961         },
9962
9963         /**
9964          * Aligns this element with another element relative to the specified anchor points. If the other element is the
9965          * document it aligns it to the viewport.
9966          * The position parameter is optional, and can be specified in any one of the following formats:
9967          * <ul>
9968          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9969          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9970          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
9971          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
9972          *   <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
9973          *       element's anchor point, and the second value is used as the target's anchor point.</li>
9974          * </ul>
9975          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
9976          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9977          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
9978          * that specified in order to enforce the viewport constraints.
9979          * Following are all of the supported anchor positions:
9980     <pre>
9981     Value  Description
9982     -----  -----------------------------
9983     tl     The top left corner (default)
9984     t      The center of the top edge
9985     tr     The top right corner
9986     l      The center of the left edge
9987     c      In the center of the element
9988     r      The center of the right edge
9989     bl     The bottom left corner
9990     b      The center of the bottom edge
9991     br     The bottom right corner
9992     </pre>
9993     Example Usage:
9994     <pre><code>
9995     // align el to other-el using the default positioning ("tl-bl", non-constrained)
9996     el.alignTo("other-el");
9997
9998     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9999     el.alignTo("other-el", "tr?");
10000
10001     // align the bottom right corner of el with the center left edge of other-el
10002     el.alignTo("other-el", "br-l?");
10003
10004     // align the center of el with the bottom left corner of other-el and
10005     // adjust the x position by -6 pixels (and the y position by 0)
10006     el.alignTo("other-el", "c-bl", [-6, 0]);
10007     </code></pre>
10008          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10009          * @param {String} position The position to align to.
10010          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10011          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10012          * @return {Roo.Element} this
10013          */
10014         alignTo : function(element, position, offsets, animate){
10015             var xy = this.getAlignToXY(element, position, offsets);
10016             this.setXY(xy, this.preanim(arguments, 3));
10017             return this;
10018         },
10019
10020         /**
10021          * Anchors an element to another element and realigns it when the window is resized.
10022          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10023          * @param {String} position The position to align to.
10024          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10025          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
10026          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
10027          * is a number, it is used as the buffer delay (defaults to 50ms).
10028          * @param {Function} callback The function to call after the animation finishes
10029          * @return {Roo.Element} this
10030          */
10031         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
10032             var action = function(){
10033                 this.alignTo(el, alignment, offsets, animate);
10034                 Roo.callback(callback, this);
10035             };
10036             Roo.EventManager.onWindowResize(action, this);
10037             var tm = typeof monitorScroll;
10038             if(tm != 'undefined'){
10039                 Roo.EventManager.on(window, 'scroll', action, this,
10040                     {buffer: tm == 'number' ? monitorScroll : 50});
10041             }
10042             action.call(this); // align immediately
10043             return this;
10044         },
10045         /**
10046          * Clears any opacity settings from this element. Required in some cases for IE.
10047          * @return {Roo.Element} this
10048          */
10049         clearOpacity : function(){
10050             if (window.ActiveXObject) {
10051                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
10052                     this.dom.style.filter = "";
10053                 }
10054             } else {
10055                 this.dom.style.opacity = "";
10056                 this.dom.style["-moz-opacity"] = "";
10057                 this.dom.style["-khtml-opacity"] = "";
10058             }
10059             return this;
10060         },
10061
10062         /**
10063          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10064          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10065          * @return {Roo.Element} this
10066          */
10067         hide : function(animate){
10068             this.setVisible(false, this.preanim(arguments, 0));
10069             return this;
10070         },
10071
10072         /**
10073         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10074         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10075          * @return {Roo.Element} this
10076          */
10077         show : function(animate){
10078             this.setVisible(true, this.preanim(arguments, 0));
10079             return this;
10080         },
10081
10082         /**
10083          * @private Test if size has a unit, otherwise appends the default
10084          */
10085         addUnits : function(size){
10086             return Roo.Element.addUnits(size, this.defaultUnit);
10087         },
10088
10089         /**
10090          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
10091          * @return {Roo.Element} this
10092          */
10093         beginMeasure : function(){
10094             var el = this.dom;
10095             if(el.offsetWidth || el.offsetHeight){
10096                 return this; // offsets work already
10097             }
10098             var changed = [];
10099             var p = this.dom, b = document.body; // start with this element
10100             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
10101                 var pe = Roo.get(p);
10102                 if(pe.getStyle('display') == 'none'){
10103                     changed.push({el: p, visibility: pe.getStyle("visibility")});
10104                     p.style.visibility = "hidden";
10105                     p.style.display = "block";
10106                 }
10107                 p = p.parentNode;
10108             }
10109             this._measureChanged = changed;
10110             return this;
10111
10112         },
10113
10114         /**
10115          * Restores displays to before beginMeasure was called
10116          * @return {Roo.Element} this
10117          */
10118         endMeasure : function(){
10119             var changed = this._measureChanged;
10120             if(changed){
10121                 for(var i = 0, len = changed.length; i < len; i++) {
10122                     var r = changed[i];
10123                     r.el.style.visibility = r.visibility;
10124                     r.el.style.display = "none";
10125                 }
10126                 this._measureChanged = null;
10127             }
10128             return this;
10129         },
10130
10131         /**
10132         * Update the innerHTML of this element, optionally searching for and processing scripts
10133         * @param {String} html The new HTML
10134         * @param {Boolean} loadScripts (optional) true to look for and process scripts
10135         * @param {Function} callback For async script loading you can be noticed when the update completes
10136         * @return {Roo.Element} this
10137          */
10138         update : function(html, loadScripts, callback){
10139             if(typeof html == "undefined"){
10140                 html = "";
10141             }
10142             if(loadScripts !== true){
10143                 this.dom.innerHTML = html;
10144                 if(typeof callback == "function"){
10145                     callback();
10146                 }
10147                 return this;
10148             }
10149             var id = Roo.id();
10150             var dom = this.dom;
10151
10152             html += '<span id="' + id + '"></span>';
10153
10154             E.onAvailable(id, function(){
10155                 var hd = document.getElementsByTagName("head")[0];
10156                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
10157                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
10158                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
10159
10160                 var match;
10161                 while(match = re.exec(html)){
10162                     var attrs = match[1];
10163                     var srcMatch = attrs ? attrs.match(srcRe) : false;
10164                     if(srcMatch && srcMatch[2]){
10165                        var s = document.createElement("script");
10166                        s.src = srcMatch[2];
10167                        var typeMatch = attrs.match(typeRe);
10168                        if(typeMatch && typeMatch[2]){
10169                            s.type = typeMatch[2];
10170                        }
10171                        hd.appendChild(s);
10172                     }else if(match[2] && match[2].length > 0){
10173                         if(window.execScript) {
10174                            window.execScript(match[2]);
10175                         } else {
10176                             /**
10177                              * eval:var:id
10178                              * eval:var:dom
10179                              * eval:var:html
10180                              * 
10181                              */
10182                            window.eval(match[2]);
10183                         }
10184                     }
10185                 }
10186                 var el = document.getElementById(id);
10187                 if(el){el.parentNode.removeChild(el);}
10188                 if(typeof callback == "function"){
10189                     callback();
10190                 }
10191             });
10192             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
10193             return this;
10194         },
10195
10196         /**
10197          * Direct access to the UpdateManager update() method (takes the same parameters).
10198          * @param {String/Function} url The url for this request or a function to call to get the url
10199          * @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}
10200          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10201          * @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.
10202          * @return {Roo.Element} this
10203          */
10204         load : function(){
10205             var um = this.getUpdateManager();
10206             um.update.apply(um, arguments);
10207             return this;
10208         },
10209
10210         /**
10211         * Gets this element's UpdateManager
10212         * @return {Roo.UpdateManager} The UpdateManager
10213         */
10214         getUpdateManager : function(){
10215             if(!this.updateManager){
10216                 this.updateManager = new Roo.UpdateManager(this);
10217             }
10218             return this.updateManager;
10219         },
10220
10221         /**
10222          * Disables text selection for this element (normalized across browsers)
10223          * @return {Roo.Element} this
10224          */
10225         unselectable : function(){
10226             this.dom.unselectable = "on";
10227             this.swallowEvent("selectstart", true);
10228             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
10229             this.addClass("x-unselectable");
10230             return this;
10231         },
10232
10233         /**
10234         * Calculates the x, y to center this element on the screen
10235         * @return {Array} The x, y values [x, y]
10236         */
10237         getCenterXY : function(){
10238             return this.getAlignToXY(document, 'c-c');
10239         },
10240
10241         /**
10242         * Centers the Element in either the viewport, or another Element.
10243         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
10244         */
10245         center : function(centerIn){
10246             this.alignTo(centerIn || document, 'c-c');
10247             return this;
10248         },
10249
10250         /**
10251          * Tests various css rules/browsers to determine if this element uses a border box
10252          * @return {Boolean}
10253          */
10254         isBorderBox : function(){
10255             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
10256         },
10257
10258         /**
10259          * Return a box {x, y, width, height} that can be used to set another elements
10260          * size/location to match this element.
10261          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
10262          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
10263          * @return {Object} box An object in the format {x, y, width, height}
10264          */
10265         getBox : function(contentBox, local){
10266             var xy;
10267             if(!local){
10268                 xy = this.getXY();
10269             }else{
10270                 var left = parseInt(this.getStyle("left"), 10) || 0;
10271                 var top = parseInt(this.getStyle("top"), 10) || 0;
10272                 xy = [left, top];
10273             }
10274             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
10275             if(!contentBox){
10276                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
10277             }else{
10278                 var l = this.getBorderWidth("l")+this.getPadding("l");
10279                 var r = this.getBorderWidth("r")+this.getPadding("r");
10280                 var t = this.getBorderWidth("t")+this.getPadding("t");
10281                 var b = this.getBorderWidth("b")+this.getPadding("b");
10282                 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)};
10283             }
10284             bx.right = bx.x + bx.width;
10285             bx.bottom = bx.y + bx.height;
10286             return bx;
10287         },
10288
10289         /**
10290          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
10291          for more information about the sides.
10292          * @param {String} sides
10293          * @return {Number}
10294          */
10295         getFrameWidth : function(sides, onlyContentBox){
10296             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
10297         },
10298
10299         /**
10300          * 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.
10301          * @param {Object} box The box to fill {x, y, width, height}
10302          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
10303          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10304          * @return {Roo.Element} this
10305          */
10306         setBox : function(box, adjust, animate){
10307             var w = box.width, h = box.height;
10308             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
10309                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
10310                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
10311             }
10312             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
10313             return this;
10314         },
10315
10316         /**
10317          * Forces the browser to repaint this element
10318          * @return {Roo.Element} this
10319          */
10320          repaint : function(){
10321             var dom = this.dom;
10322             this.addClass("x-repaint");
10323             setTimeout(function(){
10324                 Roo.get(dom).removeClass("x-repaint");
10325             }, 1);
10326             return this;
10327         },
10328
10329         /**
10330          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10331          * then it returns the calculated width of the sides (see getPadding)
10332          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10333          * @return {Object/Number}
10334          */
10335         getMargins : function(side){
10336             if(!side){
10337                 return {
10338                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10339                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10340                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10341                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10342                 };
10343             }else{
10344                 return this.addStyles(side, El.margins);
10345              }
10346         },
10347
10348         // private
10349         addStyles : function(sides, styles){
10350             var val = 0, v, w;
10351             for(var i = 0, len = sides.length; i < len; i++){
10352                 v = this.getStyle(styles[sides.charAt(i)]);
10353                 if(v){
10354                      w = parseInt(v, 10);
10355                      if(w){ val += w; }
10356                 }
10357             }
10358             return val;
10359         },
10360
10361         /**
10362          * Creates a proxy element of this element
10363          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10364          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10365          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10366          * @return {Roo.Element} The new proxy element
10367          */
10368         createProxy : function(config, renderTo, matchBox){
10369             if(renderTo){
10370                 renderTo = Roo.getDom(renderTo);
10371             }else{
10372                 renderTo = document.body;
10373             }
10374             config = typeof config == "object" ?
10375                 config : {tag : "div", cls: config};
10376             var proxy = Roo.DomHelper.append(renderTo, config, true);
10377             if(matchBox){
10378                proxy.setBox(this.getBox());
10379             }
10380             return proxy;
10381         },
10382
10383         /**
10384          * Puts a mask over this element to disable user interaction. Requires core.css.
10385          * This method can only be applied to elements which accept child nodes.
10386          * @param {String} msg (optional) A message to display in the mask
10387          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10388          * @return {Element} The mask  element
10389          */
10390         mask : function(msg, msgCls)
10391         {
10392             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10393                 this.setStyle("position", "relative");
10394             }
10395             if(!this._mask){
10396                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10397             }
10398             
10399             this.addClass("x-masked");
10400             this._mask.setDisplayed(true);
10401             
10402             // we wander
10403             var z = 0;
10404             var dom = this.dom;
10405             while (dom && dom.style) {
10406                 if (!isNaN(parseInt(dom.style.zIndex))) {
10407                     z = Math.max(z, parseInt(dom.style.zIndex));
10408                 }
10409                 dom = dom.parentNode;
10410             }
10411             // if we are masking the body - then it hides everything..
10412             if (this.dom == document.body) {
10413                 z = 1000000;
10414                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10415                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10416             }
10417            
10418             if(typeof msg == 'string'){
10419                 if(!this._maskMsg){
10420                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10421                         cls: "roo-el-mask-msg", 
10422                         cn: [
10423                             {
10424                                 tag: 'i',
10425                                 cls: 'fa fa-spinner fa-spin'
10426                             },
10427                             {
10428                                 tag: 'div'
10429                             }   
10430                         ]
10431                     }, true);
10432                 }
10433                 var mm = this._maskMsg;
10434                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10435                 if (mm.dom.lastChild) { // weird IE issue?
10436                     mm.dom.lastChild.innerHTML = msg;
10437                 }
10438                 mm.setDisplayed(true);
10439                 mm.center(this);
10440                 mm.setStyle('z-index', z + 102);
10441             }
10442             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10443                 this._mask.setHeight(this.getHeight());
10444             }
10445             this._mask.setStyle('z-index', z + 100);
10446             
10447             return this._mask;
10448         },
10449
10450         /**
10451          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10452          * it is cached for reuse.
10453          */
10454         unmask : function(removeEl){
10455             if(this._mask){
10456                 if(removeEl === true){
10457                     this._mask.remove();
10458                     delete this._mask;
10459                     if(this._maskMsg){
10460                         this._maskMsg.remove();
10461                         delete this._maskMsg;
10462                     }
10463                 }else{
10464                     this._mask.setDisplayed(false);
10465                     if(this._maskMsg){
10466                         this._maskMsg.setDisplayed(false);
10467                     }
10468                 }
10469             }
10470             this.removeClass("x-masked");
10471         },
10472
10473         /**
10474          * Returns true if this element is masked
10475          * @return {Boolean}
10476          */
10477         isMasked : function(){
10478             return this._mask && this._mask.isVisible();
10479         },
10480
10481         /**
10482          * Creates an iframe shim for this element to keep selects and other windowed objects from
10483          * showing through.
10484          * @return {Roo.Element} The new shim element
10485          */
10486         createShim : function(){
10487             var el = document.createElement('iframe');
10488             el.frameBorder = 'no';
10489             el.className = 'roo-shim';
10490             if(Roo.isIE && Roo.isSecure){
10491                 el.src = Roo.SSL_SECURE_URL;
10492             }
10493             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10494             shim.autoBoxAdjust = false;
10495             return shim;
10496         },
10497
10498         /**
10499          * Removes this element from the DOM and deletes it from the cache
10500          */
10501         remove : function(){
10502             if(this.dom.parentNode){
10503                 this.dom.parentNode.removeChild(this.dom);
10504             }
10505             delete El.cache[this.dom.id];
10506         },
10507
10508         /**
10509          * Sets up event handlers to add and remove a css class when the mouse is over this element
10510          * @param {String} className
10511          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10512          * mouseout events for children elements
10513          * @return {Roo.Element} this
10514          */
10515         addClassOnOver : function(className, preventFlicker){
10516             this.on("mouseover", function(){
10517                 Roo.fly(this, '_internal').addClass(className);
10518             }, this.dom);
10519             var removeFn = function(e){
10520                 if(preventFlicker !== true || !e.within(this, true)){
10521                     Roo.fly(this, '_internal').removeClass(className);
10522                 }
10523             };
10524             this.on("mouseout", removeFn, this.dom);
10525             return this;
10526         },
10527
10528         /**
10529          * Sets up event handlers to add and remove a css class when this element has the focus
10530          * @param {String} className
10531          * @return {Roo.Element} this
10532          */
10533         addClassOnFocus : function(className){
10534             this.on("focus", function(){
10535                 Roo.fly(this, '_internal').addClass(className);
10536             }, this.dom);
10537             this.on("blur", function(){
10538                 Roo.fly(this, '_internal').removeClass(className);
10539             }, this.dom);
10540             return this;
10541         },
10542         /**
10543          * 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)
10544          * @param {String} className
10545          * @return {Roo.Element} this
10546          */
10547         addClassOnClick : function(className){
10548             var dom = this.dom;
10549             this.on("mousedown", function(){
10550                 Roo.fly(dom, '_internal').addClass(className);
10551                 var d = Roo.get(document);
10552                 var fn = function(){
10553                     Roo.fly(dom, '_internal').removeClass(className);
10554                     d.removeListener("mouseup", fn);
10555                 };
10556                 d.on("mouseup", fn);
10557             });
10558             return this;
10559         },
10560
10561         /**
10562          * Stops the specified event from bubbling and optionally prevents the default action
10563          * @param {String} eventName
10564          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10565          * @return {Roo.Element} this
10566          */
10567         swallowEvent : function(eventName, preventDefault){
10568             var fn = function(e){
10569                 e.stopPropagation();
10570                 if(preventDefault){
10571                     e.preventDefault();
10572                 }
10573             };
10574             if(eventName instanceof Array){
10575                 for(var i = 0, len = eventName.length; i < len; i++){
10576                      this.on(eventName[i], fn);
10577                 }
10578                 return this;
10579             }
10580             this.on(eventName, fn);
10581             return this;
10582         },
10583
10584         /**
10585          * @private
10586          */
10587         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10588
10589         /**
10590          * Sizes this element to its parent element's dimensions performing
10591          * neccessary box adjustments.
10592          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10593          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10594          * @return {Roo.Element} this
10595          */
10596         fitToParent : function(monitorResize, targetParent) {
10597           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10598           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10599           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10600             return this;
10601           }
10602           var p = Roo.get(targetParent || this.dom.parentNode);
10603           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10604           if (monitorResize === true) {
10605             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10606             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10607           }
10608           return this;
10609         },
10610
10611         /**
10612          * Gets the next sibling, skipping text nodes
10613          * @return {HTMLElement} The next sibling or null
10614          */
10615         getNextSibling : function(){
10616             var n = this.dom.nextSibling;
10617             while(n && n.nodeType != 1){
10618                 n = n.nextSibling;
10619             }
10620             return n;
10621         },
10622
10623         /**
10624          * Gets the previous sibling, skipping text nodes
10625          * @return {HTMLElement} The previous sibling or null
10626          */
10627         getPrevSibling : function(){
10628             var n = this.dom.previousSibling;
10629             while(n && n.nodeType != 1){
10630                 n = n.previousSibling;
10631             }
10632             return n;
10633         },
10634
10635
10636         /**
10637          * Appends the passed element(s) to this element
10638          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10639          * @return {Roo.Element} this
10640          */
10641         appendChild: function(el){
10642             el = Roo.get(el);
10643             el.appendTo(this);
10644             return this;
10645         },
10646
10647         /**
10648          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10649          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10650          * automatically generated with the specified attributes.
10651          * @param {HTMLElement} insertBefore (optional) a child element of this element
10652          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10653          * @return {Roo.Element} The new child element
10654          */
10655         createChild: function(config, insertBefore, returnDom){
10656             config = config || {tag:'div'};
10657             if(insertBefore){
10658                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10659             }
10660             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10661         },
10662
10663         /**
10664          * Appends this element to the passed element
10665          * @param {String/HTMLElement/Element} el The new parent element
10666          * @return {Roo.Element} this
10667          */
10668         appendTo: function(el){
10669             el = Roo.getDom(el);
10670             el.appendChild(this.dom);
10671             return this;
10672         },
10673
10674         /**
10675          * Inserts this element before the passed element in the DOM
10676          * @param {String/HTMLElement/Element} el The element to insert before
10677          * @return {Roo.Element} this
10678          */
10679         insertBefore: function(el){
10680             el = Roo.getDom(el);
10681             el.parentNode.insertBefore(this.dom, el);
10682             return this;
10683         },
10684
10685         /**
10686          * Inserts this element after the passed element in the DOM
10687          * @param {String/HTMLElement/Element} el The element to insert after
10688          * @return {Roo.Element} this
10689          */
10690         insertAfter: function(el){
10691             el = Roo.getDom(el);
10692             el.parentNode.insertBefore(this.dom, el.nextSibling);
10693             return this;
10694         },
10695
10696         /**
10697          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10698          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10699          * @return {Roo.Element} The new child
10700          */
10701         insertFirst: function(el, returnDom){
10702             el = el || {};
10703             if(typeof el == 'object' && !el.nodeType){ // dh config
10704                 return this.createChild(el, this.dom.firstChild, returnDom);
10705             }else{
10706                 el = Roo.getDom(el);
10707                 this.dom.insertBefore(el, this.dom.firstChild);
10708                 return !returnDom ? Roo.get(el) : el;
10709             }
10710         },
10711
10712         /**
10713          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10714          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10715          * @param {String} where (optional) 'before' or 'after' defaults to before
10716          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10717          * @return {Roo.Element} the inserted Element
10718          */
10719         insertSibling: function(el, where, returnDom){
10720             where = where ? where.toLowerCase() : 'before';
10721             el = el || {};
10722             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10723
10724             if(typeof el == 'object' && !el.nodeType){ // dh config
10725                 if(where == 'after' && !this.dom.nextSibling){
10726                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10727                 }else{
10728                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10729                 }
10730
10731             }else{
10732                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10733                             where == 'before' ? this.dom : this.dom.nextSibling);
10734                 if(!returnDom){
10735                     rt = Roo.get(rt);
10736                 }
10737             }
10738             return rt;
10739         },
10740
10741         /**
10742          * Creates and wraps this element with another element
10743          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10744          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10745          * @return {HTMLElement/Element} The newly created wrapper element
10746          */
10747         wrap: function(config, returnDom){
10748             if(!config){
10749                 config = {tag: "div"};
10750             }
10751             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10752             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10753             return newEl;
10754         },
10755
10756         /**
10757          * Replaces the passed element with this element
10758          * @param {String/HTMLElement/Element} el The element to replace
10759          * @return {Roo.Element} this
10760          */
10761         replace: function(el){
10762             el = Roo.get(el);
10763             this.insertBefore(el);
10764             el.remove();
10765             return this;
10766         },
10767
10768         /**
10769          * Inserts an html fragment into this element
10770          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10771          * @param {String} html The HTML fragment
10772          * @param {Boolean} returnEl True to return an Roo.Element
10773          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10774          */
10775         insertHtml : function(where, html, returnEl){
10776             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10777             return returnEl ? Roo.get(el) : el;
10778         },
10779
10780         /**
10781          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10782          * @param {Object} o The object with the attributes
10783          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10784          * @return {Roo.Element} this
10785          */
10786         set : function(o, useSet){
10787             var el = this.dom;
10788             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10789             for(var attr in o){
10790                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10791                 if(attr=="cls"){
10792                     el.className = o["cls"];
10793                 }else{
10794                     if(useSet) {
10795                         el.setAttribute(attr, o[attr]);
10796                     } else {
10797                         el[attr] = o[attr];
10798                     }
10799                 }
10800             }
10801             if(o.style){
10802                 Roo.DomHelper.applyStyles(el, o.style);
10803             }
10804             return this;
10805         },
10806
10807         /**
10808          * Convenience method for constructing a KeyMap
10809          * @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:
10810          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10811          * @param {Function} fn The function to call
10812          * @param {Object} scope (optional) The scope of the function
10813          * @return {Roo.KeyMap} The KeyMap created
10814          */
10815         addKeyListener : function(key, fn, scope){
10816             var config;
10817             if(typeof key != "object" || key instanceof Array){
10818                 config = {
10819                     key: key,
10820                     fn: fn,
10821                     scope: scope
10822                 };
10823             }else{
10824                 config = {
10825                     key : key.key,
10826                     shift : key.shift,
10827                     ctrl : key.ctrl,
10828                     alt : key.alt,
10829                     fn: fn,
10830                     scope: scope
10831                 };
10832             }
10833             return new Roo.KeyMap(this, config);
10834         },
10835
10836         /**
10837          * Creates a KeyMap for this element
10838          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10839          * @return {Roo.KeyMap} The KeyMap created
10840          */
10841         addKeyMap : function(config){
10842             return new Roo.KeyMap(this, config);
10843         },
10844
10845         /**
10846          * Returns true if this element is scrollable.
10847          * @return {Boolean}
10848          */
10849          isScrollable : function(){
10850             var dom = this.dom;
10851             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10852         },
10853
10854         /**
10855          * 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().
10856          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10857          * @param {Number} value The new scroll value
10858          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10859          * @return {Element} this
10860          */
10861
10862         scrollTo : function(side, value, animate){
10863             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10864             if(!animate || !A){
10865                 this.dom[prop] = value;
10866             }else{
10867                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10868                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10869             }
10870             return this;
10871         },
10872
10873         /**
10874          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10875          * within this element's scrollable range.
10876          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10877          * @param {Number} distance How far to scroll the element in pixels
10878          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10879          * @return {Boolean} Returns true if a scroll was triggered or false if the element
10880          * was scrolled as far as it could go.
10881          */
10882          scroll : function(direction, distance, animate){
10883              if(!this.isScrollable()){
10884                  return;
10885              }
10886              var el = this.dom;
10887              var l = el.scrollLeft, t = el.scrollTop;
10888              var w = el.scrollWidth, h = el.scrollHeight;
10889              var cw = el.clientWidth, ch = el.clientHeight;
10890              direction = direction.toLowerCase();
10891              var scrolled = false;
10892              var a = this.preanim(arguments, 2);
10893              switch(direction){
10894                  case "l":
10895                  case "left":
10896                      if(w - l > cw){
10897                          var v = Math.min(l + distance, w-cw);
10898                          this.scrollTo("left", v, a);
10899                          scrolled = true;
10900                      }
10901                      break;
10902                 case "r":
10903                 case "right":
10904                      if(l > 0){
10905                          var v = Math.max(l - distance, 0);
10906                          this.scrollTo("left", v, a);
10907                          scrolled = true;
10908                      }
10909                      break;
10910                 case "t":
10911                 case "top":
10912                 case "up":
10913                      if(t > 0){
10914                          var v = Math.max(t - distance, 0);
10915                          this.scrollTo("top", v, a);
10916                          scrolled = true;
10917                      }
10918                      break;
10919                 case "b":
10920                 case "bottom":
10921                 case "down":
10922                      if(h - t > ch){
10923                          var v = Math.min(t + distance, h-ch);
10924                          this.scrollTo("top", v, a);
10925                          scrolled = true;
10926                      }
10927                      break;
10928              }
10929              return scrolled;
10930         },
10931
10932         /**
10933          * Translates the passed page coordinates into left/top css values for this element
10934          * @param {Number/Array} x The page x or an array containing [x, y]
10935          * @param {Number} y The page y
10936          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10937          */
10938         translatePoints : function(x, y){
10939             if(typeof x == 'object' || x instanceof Array){
10940                 y = x[1]; x = x[0];
10941             }
10942             var p = this.getStyle('position');
10943             var o = this.getXY();
10944
10945             var l = parseInt(this.getStyle('left'), 10);
10946             var t = parseInt(this.getStyle('top'), 10);
10947
10948             if(isNaN(l)){
10949                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10950             }
10951             if(isNaN(t)){
10952                 t = (p == "relative") ? 0 : this.dom.offsetTop;
10953             }
10954
10955             return {left: (x - o[0] + l), top: (y - o[1] + t)};
10956         },
10957
10958         /**
10959          * Returns the current scroll position of the element.
10960          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10961          */
10962         getScroll : function(){
10963             var d = this.dom, doc = document;
10964             if(d == doc || d == doc.body){
10965                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10966                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10967                 return {left: l, top: t};
10968             }else{
10969                 return {left: d.scrollLeft, top: d.scrollTop};
10970             }
10971         },
10972
10973         /**
10974          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10975          * are convert to standard 6 digit hex color.
10976          * @param {String} attr The css attribute
10977          * @param {String} defaultValue The default value to use when a valid color isn't found
10978          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10979          * YUI color anims.
10980          */
10981         getColor : function(attr, defaultValue, prefix){
10982             var v = this.getStyle(attr);
10983             if(!v || v == "transparent" || v == "inherit") {
10984                 return defaultValue;
10985             }
10986             var color = typeof prefix == "undefined" ? "#" : prefix;
10987             if(v.substr(0, 4) == "rgb("){
10988                 var rvs = v.slice(4, v.length -1).split(",");
10989                 for(var i = 0; i < 3; i++){
10990                     var h = parseInt(rvs[i]).toString(16);
10991                     if(h < 16){
10992                         h = "0" + h;
10993                     }
10994                     color += h;
10995                 }
10996             } else {
10997                 if(v.substr(0, 1) == "#"){
10998                     if(v.length == 4) {
10999                         for(var i = 1; i < 4; i++){
11000                             var c = v.charAt(i);
11001                             color +=  c + c;
11002                         }
11003                     }else if(v.length == 7){
11004                         color += v.substr(1);
11005                     }
11006                 }
11007             }
11008             return(color.length > 5 ? color.toLowerCase() : defaultValue);
11009         },
11010
11011         /**
11012          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
11013          * gradient background, rounded corners and a 4-way shadow.
11014          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
11015          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
11016          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
11017          * @return {Roo.Element} this
11018          */
11019         boxWrap : function(cls){
11020             cls = cls || 'x-box';
11021             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
11022             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
11023             return el;
11024         },
11025
11026         /**
11027          * Returns the value of a namespaced attribute from the element's underlying DOM node.
11028          * @param {String} namespace The namespace in which to look for the attribute
11029          * @param {String} name The attribute name
11030          * @return {String} The attribute value
11031          */
11032         getAttributeNS : Roo.isIE ? function(ns, name){
11033             var d = this.dom;
11034             var type = typeof d[ns+":"+name];
11035             if(type != 'undefined' && type != 'unknown'){
11036                 return d[ns+":"+name];
11037             }
11038             return d[name];
11039         } : function(ns, name){
11040             var d = this.dom;
11041             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
11042         },
11043         
11044         
11045         /**
11046          * Sets or Returns the value the dom attribute value
11047          * @param {String|Object} name The attribute name (or object to set multiple attributes)
11048          * @param {String} value (optional) The value to set the attribute to
11049          * @return {String} The attribute value
11050          */
11051         attr : function(name){
11052             if (arguments.length > 1) {
11053                 this.dom.setAttribute(name, arguments[1]);
11054                 return arguments[1];
11055             }
11056             if (typeof(name) == 'object') {
11057                 for(var i in name) {
11058                     this.attr(i, name[i]);
11059                 }
11060                 return name;
11061             }
11062             
11063             
11064             if (!this.dom.hasAttribute(name)) {
11065                 return undefined;
11066             }
11067             return this.dom.getAttribute(name);
11068         }
11069         
11070         
11071         
11072     };
11073
11074     var ep = El.prototype;
11075
11076     /**
11077      * Appends an event handler (Shorthand for addListener)
11078      * @param {String}   eventName     The type of event to append
11079      * @param {Function} fn        The method the event invokes
11080      * @param {Object} scope       (optional) The scope (this object) of the fn
11081      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
11082      * @method
11083      */
11084     ep.on = ep.addListener;
11085         // backwards compat
11086     ep.mon = ep.addListener;
11087
11088     /**
11089      * Removes an event handler from this element (shorthand for removeListener)
11090      * @param {String} eventName the type of event to remove
11091      * @param {Function} fn the method the event invokes
11092      * @return {Roo.Element} this
11093      * @method
11094      */
11095     ep.un = ep.removeListener;
11096
11097     /**
11098      * true to automatically adjust width and height settings for box-model issues (default to true)
11099      */
11100     ep.autoBoxAdjust = true;
11101
11102     // private
11103     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
11104
11105     // private
11106     El.addUnits = function(v, defaultUnit){
11107         if(v === "" || v == "auto"){
11108             return v;
11109         }
11110         if(v === undefined){
11111             return '';
11112         }
11113         if(typeof v == "number" || !El.unitPattern.test(v)){
11114             return v + (defaultUnit || 'px');
11115         }
11116         return v;
11117     };
11118
11119     // special markup used throughout Roo when box wrapping elements
11120     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>';
11121     /**
11122      * Visibility mode constant - Use visibility to hide element
11123      * @static
11124      * @type Number
11125      */
11126     El.VISIBILITY = 1;
11127     /**
11128      * Visibility mode constant - Use display to hide element
11129      * @static
11130      * @type Number
11131      */
11132     El.DISPLAY = 2;
11133
11134     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
11135     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
11136     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
11137
11138
11139
11140     /**
11141      * @private
11142      */
11143     El.cache = {};
11144
11145     var docEl;
11146
11147     /**
11148      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11149      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11150      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11151      * @return {Element} The Element object
11152      * @static
11153      */
11154     El.get = function(el){
11155         var ex, elm, id;
11156         if(!el){ return null; }
11157         if(typeof el == "string"){ // element id
11158             if(!(elm = document.getElementById(el))){
11159                 return null;
11160             }
11161             if(ex = El.cache[el]){
11162                 ex.dom = elm;
11163             }else{
11164                 ex = El.cache[el] = new El(elm);
11165             }
11166             return ex;
11167         }else if(el.tagName){ // dom element
11168             if(!(id = el.id)){
11169                 id = Roo.id(el);
11170             }
11171             if(ex = El.cache[id]){
11172                 ex.dom = el;
11173             }else{
11174                 ex = El.cache[id] = new El(el);
11175             }
11176             return ex;
11177         }else if(el instanceof El){
11178             if(el != docEl){
11179                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
11180                                                               // catch case where it hasn't been appended
11181                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
11182             }
11183             return el;
11184         }else if(el.isComposite){
11185             return el;
11186         }else if(el instanceof Array){
11187             return El.select(el);
11188         }else if(el == document){
11189             // create a bogus element object representing the document object
11190             if(!docEl){
11191                 var f = function(){};
11192                 f.prototype = El.prototype;
11193                 docEl = new f();
11194                 docEl.dom = document;
11195             }
11196             return docEl;
11197         }
11198         return null;
11199     };
11200
11201     // private
11202     El.uncache = function(el){
11203         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
11204             if(a[i]){
11205                 delete El.cache[a[i].id || a[i]];
11206             }
11207         }
11208     };
11209
11210     // private
11211     // Garbage collection - uncache elements/purge listeners on orphaned elements
11212     // so we don't hold a reference and cause the browser to retain them
11213     El.garbageCollect = function(){
11214         if(!Roo.enableGarbageCollector){
11215             clearInterval(El.collectorThread);
11216             return;
11217         }
11218         for(var eid in El.cache){
11219             var el = El.cache[eid], d = el.dom;
11220             // -------------------------------------------------------
11221             // Determining what is garbage:
11222             // -------------------------------------------------------
11223             // !d
11224             // dom node is null, definitely garbage
11225             // -------------------------------------------------------
11226             // !d.parentNode
11227             // no parentNode == direct orphan, definitely garbage
11228             // -------------------------------------------------------
11229             // !d.offsetParent && !document.getElementById(eid)
11230             // display none elements have no offsetParent so we will
11231             // also try to look it up by it's id. However, check
11232             // offsetParent first so we don't do unneeded lookups.
11233             // This enables collection of elements that are not orphans
11234             // directly, but somewhere up the line they have an orphan
11235             // parent.
11236             // -------------------------------------------------------
11237             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
11238                 delete El.cache[eid];
11239                 if(d && Roo.enableListenerCollection){
11240                     E.purgeElement(d);
11241                 }
11242             }
11243         }
11244     }
11245     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
11246
11247
11248     // dom is optional
11249     El.Flyweight = function(dom){
11250         this.dom = dom;
11251     };
11252     El.Flyweight.prototype = El.prototype;
11253
11254     El._flyweights = {};
11255     /**
11256      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11257      * the dom node can be overwritten by other code.
11258      * @param {String/HTMLElement} el The dom node or id
11259      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11260      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11261      * @static
11262      * @return {Element} The shared Element object
11263      */
11264     El.fly = function(el, named){
11265         named = named || '_global';
11266         el = Roo.getDom(el);
11267         if(!el){
11268             return null;
11269         }
11270         if(!El._flyweights[named]){
11271             El._flyweights[named] = new El.Flyweight();
11272         }
11273         El._flyweights[named].dom = el;
11274         return El._flyweights[named];
11275     };
11276
11277     /**
11278      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11279      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11280      * Shorthand of {@link Roo.Element#get}
11281      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11282      * @return {Element} The Element object
11283      * @member Roo
11284      * @method get
11285      */
11286     Roo.get = El.get;
11287     /**
11288      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11289      * the dom node can be overwritten by other code.
11290      * Shorthand of {@link Roo.Element#fly}
11291      * @param {String/HTMLElement} el The dom node or id
11292      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11293      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11294      * @static
11295      * @return {Element} The shared Element object
11296      * @member Roo
11297      * @method fly
11298      */
11299     Roo.fly = El.fly;
11300
11301     // speedy lookup for elements never to box adjust
11302     var noBoxAdjust = Roo.isStrict ? {
11303         select:1
11304     } : {
11305         input:1, select:1, textarea:1
11306     };
11307     if(Roo.isIE || Roo.isGecko){
11308         noBoxAdjust['button'] = 1;
11309     }
11310
11311
11312     Roo.EventManager.on(window, 'unload', function(){
11313         delete El.cache;
11314         delete El._flyweights;
11315     });
11316 })();
11317
11318
11319
11320
11321 if(Roo.DomQuery){
11322     Roo.Element.selectorFunction = Roo.DomQuery.select;
11323 }
11324
11325 Roo.Element.select = function(selector, unique, root){
11326     var els;
11327     if(typeof selector == "string"){
11328         els = Roo.Element.selectorFunction(selector, root);
11329     }else if(selector.length !== undefined){
11330         els = selector;
11331     }else{
11332         throw "Invalid selector";
11333     }
11334     if(unique === true){
11335         return new Roo.CompositeElement(els);
11336     }else{
11337         return new Roo.CompositeElementLite(els);
11338     }
11339 };
11340 /**
11341  * Selects elements based on the passed CSS selector to enable working on them as 1.
11342  * @param {String/Array} selector The CSS selector or an array of elements
11343  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11344  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11345  * @return {CompositeElementLite/CompositeElement}
11346  * @member Roo
11347  * @method select
11348  */
11349 Roo.select = Roo.Element.select;
11350
11351
11352
11353
11354
11355
11356
11357
11358
11359
11360
11361
11362
11363
11364 /*
11365  * Based on:
11366  * Ext JS Library 1.1.1
11367  * Copyright(c) 2006-2007, Ext JS, LLC.
11368  *
11369  * Originally Released Under LGPL - original licence link has changed is not relivant.
11370  *
11371  * Fork - LGPL
11372  * <script type="text/javascript">
11373  */
11374
11375
11376
11377 //Notifies Element that fx methods are available
11378 Roo.enableFx = true;
11379
11380 /**
11381  * @class Roo.Fx
11382  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11383  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11384  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11385  * Element effects to work.</p><br/>
11386  *
11387  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11388  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11389  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11390  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11391  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11392  * expected results and should be done with care.</p><br/>
11393  *
11394  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11395  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11396 <pre>
11397 Value  Description
11398 -----  -----------------------------
11399 tl     The top left corner
11400 t      The center of the top edge
11401 tr     The top right corner
11402 l      The center of the left edge
11403 r      The center of the right edge
11404 bl     The bottom left corner
11405 b      The center of the bottom edge
11406 br     The bottom right corner
11407 </pre>
11408  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11409  * below are common options that can be passed to any Fx method.</b>
11410  * @cfg {Function} callback A function called when the effect is finished
11411  * @cfg {Object} scope The scope of the effect function
11412  * @cfg {String} easing A valid Easing value for the effect
11413  * @cfg {String} afterCls A css class to apply after the effect
11414  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11415  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11416  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11417  * effects that end with the element being visually hidden, ignored otherwise)
11418  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11419  * a function which returns such a specification that will be applied to the Element after the effect finishes
11420  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11421  * @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
11422  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11423  */
11424 Roo.Fx = {
11425         /**
11426          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11427          * origin for the slide effect.  This function automatically handles wrapping the element with
11428          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11429          * Usage:
11430          *<pre><code>
11431 // default: slide the element in from the top
11432 el.slideIn();
11433
11434 // custom: slide the element in from the right with a 2-second duration
11435 el.slideIn('r', { duration: 2 });
11436
11437 // common config options shown with default values
11438 el.slideIn('t', {
11439     easing: 'easeOut',
11440     duration: .5
11441 });
11442 </code></pre>
11443          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11444          * @param {Object} options (optional) Object literal with any of the Fx config options
11445          * @return {Roo.Element} The Element
11446          */
11447     slideIn : function(anchor, o){
11448         var el = this.getFxEl();
11449         o = o || {};
11450
11451         el.queueFx(o, function(){
11452
11453             anchor = anchor || "t";
11454
11455             // fix display to visibility
11456             this.fixDisplay();
11457
11458             // restore values after effect
11459             var r = this.getFxRestore();
11460             var b = this.getBox();
11461             // fixed size for slide
11462             this.setSize(b);
11463
11464             // wrap if needed
11465             var wrap = this.fxWrap(r.pos, o, "hidden");
11466
11467             var st = this.dom.style;
11468             st.visibility = "visible";
11469             st.position = "absolute";
11470
11471             // clear out temp styles after slide and unwrap
11472             var after = function(){
11473                 el.fxUnwrap(wrap, r.pos, o);
11474                 st.width = r.width;
11475                 st.height = r.height;
11476                 el.afterFx(o);
11477             };
11478             // time to calc the positions
11479             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11480
11481             switch(anchor.toLowerCase()){
11482                 case "t":
11483                     wrap.setSize(b.width, 0);
11484                     st.left = st.bottom = "0";
11485                     a = {height: bh};
11486                 break;
11487                 case "l":
11488                     wrap.setSize(0, b.height);
11489                     st.right = st.top = "0";
11490                     a = {width: bw};
11491                 break;
11492                 case "r":
11493                     wrap.setSize(0, b.height);
11494                     wrap.setX(b.right);
11495                     st.left = st.top = "0";
11496                     a = {width: bw, points: pt};
11497                 break;
11498                 case "b":
11499                     wrap.setSize(b.width, 0);
11500                     wrap.setY(b.bottom);
11501                     st.left = st.top = "0";
11502                     a = {height: bh, points: pt};
11503                 break;
11504                 case "tl":
11505                     wrap.setSize(0, 0);
11506                     st.right = st.bottom = "0";
11507                     a = {width: bw, height: bh};
11508                 break;
11509                 case "bl":
11510                     wrap.setSize(0, 0);
11511                     wrap.setY(b.y+b.height);
11512                     st.right = st.top = "0";
11513                     a = {width: bw, height: bh, points: pt};
11514                 break;
11515                 case "br":
11516                     wrap.setSize(0, 0);
11517                     wrap.setXY([b.right, b.bottom]);
11518                     st.left = st.top = "0";
11519                     a = {width: bw, height: bh, points: pt};
11520                 break;
11521                 case "tr":
11522                     wrap.setSize(0, 0);
11523                     wrap.setX(b.x+b.width);
11524                     st.left = st.bottom = "0";
11525                     a = {width: bw, height: bh, points: pt};
11526                 break;
11527             }
11528             this.dom.style.visibility = "visible";
11529             wrap.show();
11530
11531             arguments.callee.anim = wrap.fxanim(a,
11532                 o,
11533                 'motion',
11534                 .5,
11535                 'easeOut', after);
11536         });
11537         return this;
11538     },
11539     
11540         /**
11541          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11542          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11543          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11544          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11545          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11546          * Usage:
11547          *<pre><code>
11548 // default: slide the element out to the top
11549 el.slideOut();
11550
11551 // custom: slide the element out to the right with a 2-second duration
11552 el.slideOut('r', { duration: 2 });
11553
11554 // common config options shown with default values
11555 el.slideOut('t', {
11556     easing: 'easeOut',
11557     duration: .5,
11558     remove: false,
11559     useDisplay: false
11560 });
11561 </code></pre>
11562          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11563          * @param {Object} options (optional) Object literal with any of the Fx config options
11564          * @return {Roo.Element} The Element
11565          */
11566     slideOut : function(anchor, o){
11567         var el = this.getFxEl();
11568         o = o || {};
11569
11570         el.queueFx(o, function(){
11571
11572             anchor = anchor || "t";
11573
11574             // restore values after effect
11575             var r = this.getFxRestore();
11576             
11577             var b = this.getBox();
11578             // fixed size for slide
11579             this.setSize(b);
11580
11581             // wrap if needed
11582             var wrap = this.fxWrap(r.pos, o, "visible");
11583
11584             var st = this.dom.style;
11585             st.visibility = "visible";
11586             st.position = "absolute";
11587
11588             wrap.setSize(b);
11589
11590             var after = function(){
11591                 if(o.useDisplay){
11592                     el.setDisplayed(false);
11593                 }else{
11594                     el.hide();
11595                 }
11596
11597                 el.fxUnwrap(wrap, r.pos, o);
11598
11599                 st.width = r.width;
11600                 st.height = r.height;
11601
11602                 el.afterFx(o);
11603             };
11604
11605             var a, zero = {to: 0};
11606             switch(anchor.toLowerCase()){
11607                 case "t":
11608                     st.left = st.bottom = "0";
11609                     a = {height: zero};
11610                 break;
11611                 case "l":
11612                     st.right = st.top = "0";
11613                     a = {width: zero};
11614                 break;
11615                 case "r":
11616                     st.left = st.top = "0";
11617                     a = {width: zero, points: {to:[b.right, b.y]}};
11618                 break;
11619                 case "b":
11620                     st.left = st.top = "0";
11621                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11622                 break;
11623                 case "tl":
11624                     st.right = st.bottom = "0";
11625                     a = {width: zero, height: zero};
11626                 break;
11627                 case "bl":
11628                     st.right = st.top = "0";
11629                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11630                 break;
11631                 case "br":
11632                     st.left = st.top = "0";
11633                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11634                 break;
11635                 case "tr":
11636                     st.left = st.bottom = "0";
11637                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11638                 break;
11639             }
11640
11641             arguments.callee.anim = wrap.fxanim(a,
11642                 o,
11643                 'motion',
11644                 .5,
11645                 "easeOut", after);
11646         });
11647         return this;
11648     },
11649
11650         /**
11651          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11652          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11653          * The element must be removed from the DOM using the 'remove' config option if desired.
11654          * Usage:
11655          *<pre><code>
11656 // default
11657 el.puff();
11658
11659 // common config options shown with default values
11660 el.puff({
11661     easing: 'easeOut',
11662     duration: .5,
11663     remove: false,
11664     useDisplay: false
11665 });
11666 </code></pre>
11667          * @param {Object} options (optional) Object literal with any of the Fx config options
11668          * @return {Roo.Element} The Element
11669          */
11670     puff : function(o){
11671         var el = this.getFxEl();
11672         o = o || {};
11673
11674         el.queueFx(o, function(){
11675             this.clearOpacity();
11676             this.show();
11677
11678             // restore values after effect
11679             var r = this.getFxRestore();
11680             var st = this.dom.style;
11681
11682             var after = function(){
11683                 if(o.useDisplay){
11684                     el.setDisplayed(false);
11685                 }else{
11686                     el.hide();
11687                 }
11688
11689                 el.clearOpacity();
11690
11691                 el.setPositioning(r.pos);
11692                 st.width = r.width;
11693                 st.height = r.height;
11694                 st.fontSize = '';
11695                 el.afterFx(o);
11696             };
11697
11698             var width = this.getWidth();
11699             var height = this.getHeight();
11700
11701             arguments.callee.anim = this.fxanim({
11702                     width : {to: this.adjustWidth(width * 2)},
11703                     height : {to: this.adjustHeight(height * 2)},
11704                     points : {by: [-(width * .5), -(height * .5)]},
11705                     opacity : {to: 0},
11706                     fontSize: {to:200, unit: "%"}
11707                 },
11708                 o,
11709                 'motion',
11710                 .5,
11711                 "easeOut", after);
11712         });
11713         return this;
11714     },
11715
11716         /**
11717          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11718          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11719          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11720          * Usage:
11721          *<pre><code>
11722 // default
11723 el.switchOff();
11724
11725 // all config options shown with default values
11726 el.switchOff({
11727     easing: 'easeIn',
11728     duration: .3,
11729     remove: false,
11730     useDisplay: false
11731 });
11732 </code></pre>
11733          * @param {Object} options (optional) Object literal with any of the Fx config options
11734          * @return {Roo.Element} The Element
11735          */
11736     switchOff : function(o){
11737         var el = this.getFxEl();
11738         o = o || {};
11739
11740         el.queueFx(o, function(){
11741             this.clearOpacity();
11742             this.clip();
11743
11744             // restore values after effect
11745             var r = this.getFxRestore();
11746             var st = this.dom.style;
11747
11748             var after = function(){
11749                 if(o.useDisplay){
11750                     el.setDisplayed(false);
11751                 }else{
11752                     el.hide();
11753                 }
11754
11755                 el.clearOpacity();
11756                 el.setPositioning(r.pos);
11757                 st.width = r.width;
11758                 st.height = r.height;
11759
11760                 el.afterFx(o);
11761             };
11762
11763             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11764                 this.clearOpacity();
11765                 (function(){
11766                     this.fxanim({
11767                         height:{to:1},
11768                         points:{by:[0, this.getHeight() * .5]}
11769                     }, o, 'motion', 0.3, 'easeIn', after);
11770                 }).defer(100, this);
11771             });
11772         });
11773         return this;
11774     },
11775
11776     /**
11777      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11778      * changed using the "attr" config option) and then fading back to the original color. If no original
11779      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11780      * Usage:
11781 <pre><code>
11782 // default: highlight background to yellow
11783 el.highlight();
11784
11785 // custom: highlight foreground text to blue for 2 seconds
11786 el.highlight("0000ff", { attr: 'color', duration: 2 });
11787
11788 // common config options shown with default values
11789 el.highlight("ffff9c", {
11790     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11791     endColor: (current color) or "ffffff",
11792     easing: 'easeIn',
11793     duration: 1
11794 });
11795 </code></pre>
11796      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11797      * @param {Object} options (optional) Object literal with any of the Fx config options
11798      * @return {Roo.Element} The Element
11799      */ 
11800     highlight : function(color, o){
11801         var el = this.getFxEl();
11802         o = o || {};
11803
11804         el.queueFx(o, function(){
11805             color = color || "ffff9c";
11806             attr = o.attr || "backgroundColor";
11807
11808             this.clearOpacity();
11809             this.show();
11810
11811             var origColor = this.getColor(attr);
11812             var restoreColor = this.dom.style[attr];
11813             endColor = (o.endColor || origColor) || "ffffff";
11814
11815             var after = function(){
11816                 el.dom.style[attr] = restoreColor;
11817                 el.afterFx(o);
11818             };
11819
11820             var a = {};
11821             a[attr] = {from: color, to: endColor};
11822             arguments.callee.anim = this.fxanim(a,
11823                 o,
11824                 'color',
11825                 1,
11826                 'easeIn', after);
11827         });
11828         return this;
11829     },
11830
11831    /**
11832     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11833     * Usage:
11834 <pre><code>
11835 // default: a single light blue ripple
11836 el.frame();
11837
11838 // custom: 3 red ripples lasting 3 seconds total
11839 el.frame("ff0000", 3, { duration: 3 });
11840
11841 // common config options shown with default values
11842 el.frame("C3DAF9", 1, {
11843     duration: 1 //duration of entire animation (not each individual ripple)
11844     // Note: Easing is not configurable and will be ignored if included
11845 });
11846 </code></pre>
11847     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11848     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11849     * @param {Object} options (optional) Object literal with any of the Fx config options
11850     * @return {Roo.Element} The Element
11851     */
11852     frame : function(color, count, o){
11853         var el = this.getFxEl();
11854         o = o || {};
11855
11856         el.queueFx(o, function(){
11857             color = color || "#C3DAF9";
11858             if(color.length == 6){
11859                 color = "#" + color;
11860             }
11861             count = count || 1;
11862             duration = o.duration || 1;
11863             this.show();
11864
11865             var b = this.getBox();
11866             var animFn = function(){
11867                 var proxy = this.createProxy({
11868
11869                      style:{
11870                         visbility:"hidden",
11871                         position:"absolute",
11872                         "z-index":"35000", // yee haw
11873                         border:"0px solid " + color
11874                      }
11875                   });
11876                 var scale = Roo.isBorderBox ? 2 : 1;
11877                 proxy.animate({
11878                     top:{from:b.y, to:b.y - 20},
11879                     left:{from:b.x, to:b.x - 20},
11880                     borderWidth:{from:0, to:10},
11881                     opacity:{from:1, to:0},
11882                     height:{from:b.height, to:(b.height + (20*scale))},
11883                     width:{from:b.width, to:(b.width + (20*scale))}
11884                 }, duration, function(){
11885                     proxy.remove();
11886                 });
11887                 if(--count > 0){
11888                      animFn.defer((duration/2)*1000, this);
11889                 }else{
11890                     el.afterFx(o);
11891                 }
11892             };
11893             animFn.call(this);
11894         });
11895         return this;
11896     },
11897
11898    /**
11899     * Creates a pause before any subsequent queued effects begin.  If there are
11900     * no effects queued after the pause it will have no effect.
11901     * Usage:
11902 <pre><code>
11903 el.pause(1);
11904 </code></pre>
11905     * @param {Number} seconds The length of time to pause (in seconds)
11906     * @return {Roo.Element} The Element
11907     */
11908     pause : function(seconds){
11909         var el = this.getFxEl();
11910         var o = {};
11911
11912         el.queueFx(o, function(){
11913             setTimeout(function(){
11914                 el.afterFx(o);
11915             }, seconds * 1000);
11916         });
11917         return this;
11918     },
11919
11920    /**
11921     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
11922     * using the "endOpacity" config option.
11923     * Usage:
11924 <pre><code>
11925 // default: fade in from opacity 0 to 100%
11926 el.fadeIn();
11927
11928 // custom: fade in from opacity 0 to 75% over 2 seconds
11929 el.fadeIn({ endOpacity: .75, duration: 2});
11930
11931 // common config options shown with default values
11932 el.fadeIn({
11933     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11934     easing: 'easeOut',
11935     duration: .5
11936 });
11937 </code></pre>
11938     * @param {Object} options (optional) Object literal with any of the Fx config options
11939     * @return {Roo.Element} The Element
11940     */
11941     fadeIn : function(o){
11942         var el = this.getFxEl();
11943         o = o || {};
11944         el.queueFx(o, function(){
11945             this.setOpacity(0);
11946             this.fixDisplay();
11947             this.dom.style.visibility = 'visible';
11948             var to = o.endOpacity || 1;
11949             arguments.callee.anim = this.fxanim({opacity:{to:to}},
11950                 o, null, .5, "easeOut", function(){
11951                 if(to == 1){
11952                     this.clearOpacity();
11953                 }
11954                 el.afterFx(o);
11955             });
11956         });
11957         return this;
11958     },
11959
11960    /**
11961     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
11962     * using the "endOpacity" config option.
11963     * Usage:
11964 <pre><code>
11965 // default: fade out from the element's current opacity to 0
11966 el.fadeOut();
11967
11968 // custom: fade out from the element's current opacity to 25% over 2 seconds
11969 el.fadeOut({ endOpacity: .25, duration: 2});
11970
11971 // common config options shown with default values
11972 el.fadeOut({
11973     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11974     easing: 'easeOut',
11975     duration: .5
11976     remove: false,
11977     useDisplay: false
11978 });
11979 </code></pre>
11980     * @param {Object} options (optional) Object literal with any of the Fx config options
11981     * @return {Roo.Element} The Element
11982     */
11983     fadeOut : function(o){
11984         var el = this.getFxEl();
11985         o = o || {};
11986         el.queueFx(o, function(){
11987             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11988                 o, null, .5, "easeOut", function(){
11989                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11990                      this.dom.style.display = "none";
11991                 }else{
11992                      this.dom.style.visibility = "hidden";
11993                 }
11994                 this.clearOpacity();
11995                 el.afterFx(o);
11996             });
11997         });
11998         return this;
11999     },
12000
12001    /**
12002     * Animates the transition of an element's dimensions from a starting height/width
12003     * to an ending height/width.
12004     * Usage:
12005 <pre><code>
12006 // change height and width to 100x100 pixels
12007 el.scale(100, 100);
12008
12009 // common config options shown with default values.  The height and width will default to
12010 // the element's existing values if passed as null.
12011 el.scale(
12012     [element's width],
12013     [element's height], {
12014     easing: 'easeOut',
12015     duration: .35
12016 });
12017 </code></pre>
12018     * @param {Number} width  The new width (pass undefined to keep the original width)
12019     * @param {Number} height  The new height (pass undefined to keep the original height)
12020     * @param {Object} options (optional) Object literal with any of the Fx config options
12021     * @return {Roo.Element} The Element
12022     */
12023     scale : function(w, h, o){
12024         this.shift(Roo.apply({}, o, {
12025             width: w,
12026             height: h
12027         }));
12028         return this;
12029     },
12030
12031    /**
12032     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
12033     * Any of these properties not specified in the config object will not be changed.  This effect 
12034     * requires that at least one new dimension, position or opacity setting must be passed in on
12035     * the config object in order for the function to have any effect.
12036     * Usage:
12037 <pre><code>
12038 // slide the element horizontally to x position 200 while changing the height and opacity
12039 el.shift({ x: 200, height: 50, opacity: .8 });
12040
12041 // common config options shown with default values.
12042 el.shift({
12043     width: [element's width],
12044     height: [element's height],
12045     x: [element's x position],
12046     y: [element's y position],
12047     opacity: [element's opacity],
12048     easing: 'easeOut',
12049     duration: .35
12050 });
12051 </code></pre>
12052     * @param {Object} options  Object literal with any of the Fx config options
12053     * @return {Roo.Element} The Element
12054     */
12055     shift : function(o){
12056         var el = this.getFxEl();
12057         o = o || {};
12058         el.queueFx(o, function(){
12059             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
12060             if(w !== undefined){
12061                 a.width = {to: this.adjustWidth(w)};
12062             }
12063             if(h !== undefined){
12064                 a.height = {to: this.adjustHeight(h)};
12065             }
12066             if(x !== undefined || y !== undefined){
12067                 a.points = {to: [
12068                     x !== undefined ? x : this.getX(),
12069                     y !== undefined ? y : this.getY()
12070                 ]};
12071             }
12072             if(op !== undefined){
12073                 a.opacity = {to: op};
12074             }
12075             if(o.xy !== undefined){
12076                 a.points = {to: o.xy};
12077             }
12078             arguments.callee.anim = this.fxanim(a,
12079                 o, 'motion', .35, "easeOut", function(){
12080                 el.afterFx(o);
12081             });
12082         });
12083         return this;
12084     },
12085
12086         /**
12087          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
12088          * ending point of the effect.
12089          * Usage:
12090          *<pre><code>
12091 // default: slide the element downward while fading out
12092 el.ghost();
12093
12094 // custom: slide the element out to the right with a 2-second duration
12095 el.ghost('r', { duration: 2 });
12096
12097 // common config options shown with default values
12098 el.ghost('b', {
12099     easing: 'easeOut',
12100     duration: .5
12101     remove: false,
12102     useDisplay: false
12103 });
12104 </code></pre>
12105          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
12106          * @param {Object} options (optional) Object literal with any of the Fx config options
12107          * @return {Roo.Element} The Element
12108          */
12109     ghost : function(anchor, o){
12110         var el = this.getFxEl();
12111         o = o || {};
12112
12113         el.queueFx(o, function(){
12114             anchor = anchor || "b";
12115
12116             // restore values after effect
12117             var r = this.getFxRestore();
12118             var w = this.getWidth(),
12119                 h = this.getHeight();
12120
12121             var st = this.dom.style;
12122
12123             var after = function(){
12124                 if(o.useDisplay){
12125                     el.setDisplayed(false);
12126                 }else{
12127                     el.hide();
12128                 }
12129
12130                 el.clearOpacity();
12131                 el.setPositioning(r.pos);
12132                 st.width = r.width;
12133                 st.height = r.height;
12134
12135                 el.afterFx(o);
12136             };
12137
12138             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
12139             switch(anchor.toLowerCase()){
12140                 case "t":
12141                     pt.by = [0, -h];
12142                 break;
12143                 case "l":
12144                     pt.by = [-w, 0];
12145                 break;
12146                 case "r":
12147                     pt.by = [w, 0];
12148                 break;
12149                 case "b":
12150                     pt.by = [0, h];
12151                 break;
12152                 case "tl":
12153                     pt.by = [-w, -h];
12154                 break;
12155                 case "bl":
12156                     pt.by = [-w, h];
12157                 break;
12158                 case "br":
12159                     pt.by = [w, h];
12160                 break;
12161                 case "tr":
12162                     pt.by = [w, -h];
12163                 break;
12164             }
12165
12166             arguments.callee.anim = this.fxanim(a,
12167                 o,
12168                 'motion',
12169                 .5,
12170                 "easeOut", after);
12171         });
12172         return this;
12173     },
12174
12175         /**
12176          * Ensures that all effects queued after syncFx is called on the element are
12177          * run concurrently.  This is the opposite of {@link #sequenceFx}.
12178          * @return {Roo.Element} The Element
12179          */
12180     syncFx : function(){
12181         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12182             block : false,
12183             concurrent : true,
12184             stopFx : false
12185         });
12186         return this;
12187     },
12188
12189         /**
12190          * Ensures that all effects queued after sequenceFx is called on the element are
12191          * run in sequence.  This is the opposite of {@link #syncFx}.
12192          * @return {Roo.Element} The Element
12193          */
12194     sequenceFx : function(){
12195         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12196             block : false,
12197             concurrent : false,
12198             stopFx : false
12199         });
12200         return this;
12201     },
12202
12203         /* @private */
12204     nextFx : function(){
12205         var ef = this.fxQueue[0];
12206         if(ef){
12207             ef.call(this);
12208         }
12209     },
12210
12211         /**
12212          * Returns true if the element has any effects actively running or queued, else returns false.
12213          * @return {Boolean} True if element has active effects, else false
12214          */
12215     hasActiveFx : function(){
12216         return this.fxQueue && this.fxQueue[0];
12217     },
12218
12219         /**
12220          * Stops any running effects and clears the element's internal effects queue if it contains
12221          * any additional effects that haven't started yet.
12222          * @return {Roo.Element} The Element
12223          */
12224     stopFx : function(){
12225         if(this.hasActiveFx()){
12226             var cur = this.fxQueue[0];
12227             if(cur && cur.anim && cur.anim.isAnimated()){
12228                 this.fxQueue = [cur]; // clear out others
12229                 cur.anim.stop(true);
12230             }
12231         }
12232         return this;
12233     },
12234
12235         /* @private */
12236     beforeFx : function(o){
12237         if(this.hasActiveFx() && !o.concurrent){
12238            if(o.stopFx){
12239                this.stopFx();
12240                return true;
12241            }
12242            return false;
12243         }
12244         return true;
12245     },
12246
12247         /**
12248          * Returns true if the element is currently blocking so that no other effect can be queued
12249          * until this effect is finished, else returns false if blocking is not set.  This is commonly
12250          * used to ensure that an effect initiated by a user action runs to completion prior to the
12251          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
12252          * @return {Boolean} True if blocking, else false
12253          */
12254     hasFxBlock : function(){
12255         var q = this.fxQueue;
12256         return q && q[0] && q[0].block;
12257     },
12258
12259         /* @private */
12260     queueFx : function(o, fn){
12261         if(!this.fxQueue){
12262             this.fxQueue = [];
12263         }
12264         if(!this.hasFxBlock()){
12265             Roo.applyIf(o, this.fxDefaults);
12266             if(!o.concurrent){
12267                 var run = this.beforeFx(o);
12268                 fn.block = o.block;
12269                 this.fxQueue.push(fn);
12270                 if(run){
12271                     this.nextFx();
12272                 }
12273             }else{
12274                 fn.call(this);
12275             }
12276         }
12277         return this;
12278     },
12279
12280         /* @private */
12281     fxWrap : function(pos, o, vis){
12282         var wrap;
12283         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
12284             var wrapXY;
12285             if(o.fixPosition){
12286                 wrapXY = this.getXY();
12287             }
12288             var div = document.createElement("div");
12289             div.style.visibility = vis;
12290             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
12291             wrap.setPositioning(pos);
12292             if(wrap.getStyle("position") == "static"){
12293                 wrap.position("relative");
12294             }
12295             this.clearPositioning('auto');
12296             wrap.clip();
12297             wrap.dom.appendChild(this.dom);
12298             if(wrapXY){
12299                 wrap.setXY(wrapXY);
12300             }
12301         }
12302         return wrap;
12303     },
12304
12305         /* @private */
12306     fxUnwrap : function(wrap, pos, o){
12307         this.clearPositioning();
12308         this.setPositioning(pos);
12309         if(!o.wrap){
12310             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
12311             wrap.remove();
12312         }
12313     },
12314
12315         /* @private */
12316     getFxRestore : function(){
12317         var st = this.dom.style;
12318         return {pos: this.getPositioning(), width: st.width, height : st.height};
12319     },
12320
12321         /* @private */
12322     afterFx : function(o){
12323         if(o.afterStyle){
12324             this.applyStyles(o.afterStyle);
12325         }
12326         if(o.afterCls){
12327             this.addClass(o.afterCls);
12328         }
12329         if(o.remove === true){
12330             this.remove();
12331         }
12332         Roo.callback(o.callback, o.scope, [this]);
12333         if(!o.concurrent){
12334             this.fxQueue.shift();
12335             this.nextFx();
12336         }
12337     },
12338
12339         /* @private */
12340     getFxEl : function(){ // support for composite element fx
12341         return Roo.get(this.dom);
12342     },
12343
12344         /* @private */
12345     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12346         animType = animType || 'run';
12347         opt = opt || {};
12348         var anim = Roo.lib.Anim[animType](
12349             this.dom, args,
12350             (opt.duration || defaultDur) || .35,
12351             (opt.easing || defaultEase) || 'easeOut',
12352             function(){
12353                 Roo.callback(cb, this);
12354             },
12355             this
12356         );
12357         opt.anim = anim;
12358         return anim;
12359     }
12360 };
12361
12362 // backwords compat
12363 Roo.Fx.resize = Roo.Fx.scale;
12364
12365 //When included, Roo.Fx is automatically applied to Element so that all basic
12366 //effects are available directly via the Element API
12367 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12368  * Based on:
12369  * Ext JS Library 1.1.1
12370  * Copyright(c) 2006-2007, Ext JS, LLC.
12371  *
12372  * Originally Released Under LGPL - original licence link has changed is not relivant.
12373  *
12374  * Fork - LGPL
12375  * <script type="text/javascript">
12376  */
12377
12378
12379 /**
12380  * @class Roo.CompositeElement
12381  * Standard composite class. Creates a Roo.Element for every element in the collection.
12382  * <br><br>
12383  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12384  * actions will be performed on all the elements in this collection.</b>
12385  * <br><br>
12386  * All methods return <i>this</i> and can be chained.
12387  <pre><code>
12388  var els = Roo.select("#some-el div.some-class", true);
12389  // or select directly from an existing element
12390  var el = Roo.get('some-el');
12391  el.select('div.some-class', true);
12392
12393  els.setWidth(100); // all elements become 100 width
12394  els.hide(true); // all elements fade out and hide
12395  // or
12396  els.setWidth(100).hide(true);
12397  </code></pre>
12398  */
12399 Roo.CompositeElement = function(els){
12400     this.elements = [];
12401     this.addElements(els);
12402 };
12403 Roo.CompositeElement.prototype = {
12404     isComposite: true,
12405     addElements : function(els){
12406         if(!els) {
12407             return this;
12408         }
12409         if(typeof els == "string"){
12410             els = Roo.Element.selectorFunction(els);
12411         }
12412         var yels = this.elements;
12413         var index = yels.length-1;
12414         for(var i = 0, len = els.length; i < len; i++) {
12415                 yels[++index] = Roo.get(els[i]);
12416         }
12417         return this;
12418     },
12419
12420     /**
12421     * Clears this composite and adds the elements returned by the passed selector.
12422     * @param {String/Array} els A string CSS selector, an array of elements or an element
12423     * @return {CompositeElement} this
12424     */
12425     fill : function(els){
12426         this.elements = [];
12427         this.add(els);
12428         return this;
12429     },
12430
12431     /**
12432     * Filters this composite to only elements that match the passed selector.
12433     * @param {String} selector A string CSS selector
12434     * @param {Boolean} inverse return inverse filter (not matches)
12435     * @return {CompositeElement} this
12436     */
12437     filter : function(selector, inverse){
12438         var els = [];
12439         inverse = inverse || false;
12440         this.each(function(el){
12441             var match = inverse ? !el.is(selector) : el.is(selector);
12442             if(match){
12443                 els[els.length] = el.dom;
12444             }
12445         });
12446         this.fill(els);
12447         return this;
12448     },
12449
12450     invoke : function(fn, args){
12451         var els = this.elements;
12452         for(var i = 0, len = els.length; i < len; i++) {
12453                 Roo.Element.prototype[fn].apply(els[i], args);
12454         }
12455         return this;
12456     },
12457     /**
12458     * Adds elements to this composite.
12459     * @param {String/Array} els A string CSS selector, an array of elements or an element
12460     * @return {CompositeElement} this
12461     */
12462     add : function(els){
12463         if(typeof els == "string"){
12464             this.addElements(Roo.Element.selectorFunction(els));
12465         }else if(els.length !== undefined){
12466             this.addElements(els);
12467         }else{
12468             this.addElements([els]);
12469         }
12470         return this;
12471     },
12472     /**
12473     * Calls the passed function passing (el, this, index) for each element in this composite.
12474     * @param {Function} fn The function to call
12475     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12476     * @return {CompositeElement} this
12477     */
12478     each : function(fn, scope){
12479         var els = this.elements;
12480         for(var i = 0, len = els.length; i < len; i++){
12481             if(fn.call(scope || els[i], els[i], this, i) === false) {
12482                 break;
12483             }
12484         }
12485         return this;
12486     },
12487
12488     /**
12489      * Returns the Element object at the specified index
12490      * @param {Number} index
12491      * @return {Roo.Element}
12492      */
12493     item : function(index){
12494         return this.elements[index] || null;
12495     },
12496
12497     /**
12498      * Returns the first Element
12499      * @return {Roo.Element}
12500      */
12501     first : function(){
12502         return this.item(0);
12503     },
12504
12505     /**
12506      * Returns the last Element
12507      * @return {Roo.Element}
12508      */
12509     last : function(){
12510         return this.item(this.elements.length-1);
12511     },
12512
12513     /**
12514      * Returns the number of elements in this composite
12515      * @return Number
12516      */
12517     getCount : function(){
12518         return this.elements.length;
12519     },
12520
12521     /**
12522      * Returns true if this composite contains the passed element
12523      * @return Boolean
12524      */
12525     contains : function(el){
12526         return this.indexOf(el) !== -1;
12527     },
12528
12529     /**
12530      * Returns true if this composite contains the passed element
12531      * @return Boolean
12532      */
12533     indexOf : function(el){
12534         return this.elements.indexOf(Roo.get(el));
12535     },
12536
12537
12538     /**
12539     * Removes the specified element(s).
12540     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12541     * or an array of any of those.
12542     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12543     * @return {CompositeElement} this
12544     */
12545     removeElement : function(el, removeDom){
12546         if(el instanceof Array){
12547             for(var i = 0, len = el.length; i < len; i++){
12548                 this.removeElement(el[i]);
12549             }
12550             return this;
12551         }
12552         var index = typeof el == 'number' ? el : this.indexOf(el);
12553         if(index !== -1){
12554             if(removeDom){
12555                 var d = this.elements[index];
12556                 if(d.dom){
12557                     d.remove();
12558                 }else{
12559                     d.parentNode.removeChild(d);
12560                 }
12561             }
12562             this.elements.splice(index, 1);
12563         }
12564         return this;
12565     },
12566
12567     /**
12568     * Replaces the specified element with the passed element.
12569     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12570     * to replace.
12571     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12572     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12573     * @return {CompositeElement} this
12574     */
12575     replaceElement : function(el, replacement, domReplace){
12576         var index = typeof el == 'number' ? el : this.indexOf(el);
12577         if(index !== -1){
12578             if(domReplace){
12579                 this.elements[index].replaceWith(replacement);
12580             }else{
12581                 this.elements.splice(index, 1, Roo.get(replacement))
12582             }
12583         }
12584         return this;
12585     },
12586
12587     /**
12588      * Removes all elements.
12589      */
12590     clear : function(){
12591         this.elements = [];
12592     }
12593 };
12594 (function(){
12595     Roo.CompositeElement.createCall = function(proto, fnName){
12596         if(!proto[fnName]){
12597             proto[fnName] = function(){
12598                 return this.invoke(fnName, arguments);
12599             };
12600         }
12601     };
12602     for(var fnName in Roo.Element.prototype){
12603         if(typeof Roo.Element.prototype[fnName] == "function"){
12604             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12605         }
12606     };
12607 })();
12608 /*
12609  * Based on:
12610  * Ext JS Library 1.1.1
12611  * Copyright(c) 2006-2007, Ext JS, LLC.
12612  *
12613  * Originally Released Under LGPL - original licence link has changed is not relivant.
12614  *
12615  * Fork - LGPL
12616  * <script type="text/javascript">
12617  */
12618
12619 /**
12620  * @class Roo.CompositeElementLite
12621  * @extends Roo.CompositeElement
12622  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12623  <pre><code>
12624  var els = Roo.select("#some-el div.some-class");
12625  // or select directly from an existing element
12626  var el = Roo.get('some-el');
12627  el.select('div.some-class');
12628
12629  els.setWidth(100); // all elements become 100 width
12630  els.hide(true); // all elements fade out and hide
12631  // or
12632  els.setWidth(100).hide(true);
12633  </code></pre><br><br>
12634  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12635  * actions will be performed on all the elements in this collection.</b>
12636  */
12637 Roo.CompositeElementLite = function(els){
12638     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12639     this.el = new Roo.Element.Flyweight();
12640 };
12641 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12642     addElements : function(els){
12643         if(els){
12644             if(els instanceof Array){
12645                 this.elements = this.elements.concat(els);
12646             }else{
12647                 var yels = this.elements;
12648                 var index = yels.length-1;
12649                 for(var i = 0, len = els.length; i < len; i++) {
12650                     yels[++index] = els[i];
12651                 }
12652             }
12653         }
12654         return this;
12655     },
12656     invoke : function(fn, args){
12657         var els = this.elements;
12658         var el = this.el;
12659         for(var i = 0, len = els.length; i < len; i++) {
12660             el.dom = els[i];
12661                 Roo.Element.prototype[fn].apply(el, args);
12662         }
12663         return this;
12664     },
12665     /**
12666      * Returns a flyweight Element of the dom element object at the specified index
12667      * @param {Number} index
12668      * @return {Roo.Element}
12669      */
12670     item : function(index){
12671         if(!this.elements[index]){
12672             return null;
12673         }
12674         this.el.dom = this.elements[index];
12675         return this.el;
12676     },
12677
12678     // fixes scope with flyweight
12679     addListener : function(eventName, handler, scope, opt){
12680         var els = this.elements;
12681         for(var i = 0, len = els.length; i < len; i++) {
12682             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12683         }
12684         return this;
12685     },
12686
12687     /**
12688     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12689     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12690     * a reference to the dom node, use el.dom.</b>
12691     * @param {Function} fn The function to call
12692     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12693     * @return {CompositeElement} this
12694     */
12695     each : function(fn, scope){
12696         var els = this.elements;
12697         var el = this.el;
12698         for(var i = 0, len = els.length; i < len; i++){
12699             el.dom = els[i];
12700                 if(fn.call(scope || el, el, this, i) === false){
12701                 break;
12702             }
12703         }
12704         return this;
12705     },
12706
12707     indexOf : function(el){
12708         return this.elements.indexOf(Roo.getDom(el));
12709     },
12710
12711     replaceElement : function(el, replacement, domReplace){
12712         var index = typeof el == 'number' ? el : this.indexOf(el);
12713         if(index !== -1){
12714             replacement = Roo.getDom(replacement);
12715             if(domReplace){
12716                 var d = this.elements[index];
12717                 d.parentNode.insertBefore(replacement, d);
12718                 d.parentNode.removeChild(d);
12719             }
12720             this.elements.splice(index, 1, replacement);
12721         }
12722         return this;
12723     }
12724 });
12725 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12726
12727 /*
12728  * Based on:
12729  * Ext JS Library 1.1.1
12730  * Copyright(c) 2006-2007, Ext JS, LLC.
12731  *
12732  * Originally Released Under LGPL - original licence link has changed is not relivant.
12733  *
12734  * Fork - LGPL
12735  * <script type="text/javascript">
12736  */
12737
12738  
12739
12740 /**
12741  * @class Roo.data.Connection
12742  * @extends Roo.util.Observable
12743  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12744  * either to a configured URL, or to a URL specified at request time. 
12745  * 
12746  * Requests made by this class are asynchronous, and will return immediately. No data from
12747  * the server will be available to the statement immediately following the {@link #request} call.
12748  * To process returned data, use a callback in the request options object, or an event listener.
12749  * 
12750  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12751  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12752  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12753  * property and, if present, the IFRAME's XML document as the responseXML property.
12754  * 
12755  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12756  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12757  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12758  * standard DOM methods.
12759  * @constructor
12760  * @param {Object} config a configuration object.
12761  */
12762 Roo.data.Connection = function(config){
12763     Roo.apply(this, config);
12764     this.addEvents({
12765         /**
12766          * @event beforerequest
12767          * Fires before a network request is made to retrieve a data object.
12768          * @param {Connection} conn This Connection object.
12769          * @param {Object} options The options config object passed to the {@link #request} method.
12770          */
12771         "beforerequest" : true,
12772         /**
12773          * @event requestcomplete
12774          * Fires if the request was successfully completed.
12775          * @param {Connection} conn This Connection object.
12776          * @param {Object} response The XHR object containing the response data.
12777          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12778          * @param {Object} options The options config object passed to the {@link #request} method.
12779          */
12780         "requestcomplete" : true,
12781         /**
12782          * @event requestexception
12783          * Fires if an error HTTP status was returned from the server.
12784          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12785          * @param {Connection} conn This Connection object.
12786          * @param {Object} response The XHR object containing the response data.
12787          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12788          * @param {Object} options The options config object passed to the {@link #request} method.
12789          */
12790         "requestexception" : true
12791     });
12792     Roo.data.Connection.superclass.constructor.call(this);
12793 };
12794
12795 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12796     /**
12797      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12798      */
12799     /**
12800      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12801      * extra parameters to each request made by this object. (defaults to undefined)
12802      */
12803     /**
12804      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12805      *  to each request made by this object. (defaults to undefined)
12806      */
12807     /**
12808      * @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)
12809      */
12810     /**
12811      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12812      */
12813     timeout : 30000,
12814     /**
12815      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12816      * @type Boolean
12817      */
12818     autoAbort:false,
12819
12820     /**
12821      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12822      * @type Boolean
12823      */
12824     disableCaching: true,
12825
12826     /**
12827      * Sends an HTTP request to a remote server.
12828      * @param {Object} options An object which may contain the following properties:<ul>
12829      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12830      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12831      * request, a url encoded string or a function to call to get either.</li>
12832      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12833      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12834      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12835      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12836      * <li>options {Object} The parameter to the request call.</li>
12837      * <li>success {Boolean} True if the request succeeded.</li>
12838      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12839      * </ul></li>
12840      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12841      * The callback is passed the following parameters:<ul>
12842      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12843      * <li>options {Object} The parameter to the request call.</li>
12844      * </ul></li>
12845      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12846      * The callback is passed the following parameters:<ul>
12847      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12848      * <li>options {Object} The parameter to the request call.</li>
12849      * </ul></li>
12850      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12851      * for the callback function. Defaults to the browser window.</li>
12852      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12853      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12854      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12855      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12856      * params for the post data. Any params will be appended to the URL.</li>
12857      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12858      * </ul>
12859      * @return {Number} transactionId
12860      */
12861     request : function(o){
12862         if(this.fireEvent("beforerequest", this, o) !== false){
12863             var p = o.params;
12864
12865             if(typeof p == "function"){
12866                 p = p.call(o.scope||window, o);
12867             }
12868             if(typeof p == "object"){
12869                 p = Roo.urlEncode(o.params);
12870             }
12871             if(this.extraParams){
12872                 var extras = Roo.urlEncode(this.extraParams);
12873                 p = p ? (p + '&' + extras) : extras;
12874             }
12875
12876             var url = o.url || this.url;
12877             if(typeof url == 'function'){
12878                 url = url.call(o.scope||window, o);
12879             }
12880
12881             if(o.form){
12882                 var form = Roo.getDom(o.form);
12883                 url = url || form.action;
12884
12885                 var enctype = form.getAttribute("enctype");
12886                 
12887                 if (o.formData) {
12888                     return this.doFormDataUpload(o, url);
12889                 }
12890                 
12891                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12892                     return this.doFormUpload(o, p, url);
12893                 }
12894                 var f = Roo.lib.Ajax.serializeForm(form);
12895                 p = p ? (p + '&' + f) : f;
12896             }
12897             
12898             if (!o.form && o.formData) {
12899                 o.formData = o.formData === true ? new FormData() : o.formData;
12900                 for (var k in o.params) {
12901                     o.formData.append(k,o.params[k]);
12902                 }
12903                     
12904                 return this.doFormDataUpload(o, url);
12905             }
12906             
12907
12908             var hs = o.headers;
12909             if(this.defaultHeaders){
12910                 hs = Roo.apply(hs || {}, this.defaultHeaders);
12911                 if(!o.headers){
12912                     o.headers = hs;
12913                 }
12914             }
12915
12916             var cb = {
12917                 success: this.handleResponse,
12918                 failure: this.handleFailure,
12919                 scope: this,
12920                 argument: {options: o},
12921                 timeout : o.timeout || this.timeout
12922             };
12923
12924             var method = o.method||this.method||(p ? "POST" : "GET");
12925
12926             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12927                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12928             }
12929
12930             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12931                 if(o.autoAbort){
12932                     this.abort();
12933                 }
12934             }else if(this.autoAbort !== false){
12935                 this.abort();
12936             }
12937
12938             if((method == 'GET' && p) || o.xmlData){
12939                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12940                 p = '';
12941             }
12942             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12943             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12944             Roo.lib.Ajax.useDefaultHeader == true;
12945             return this.transId;
12946         }else{
12947             Roo.callback(o.callback, o.scope, [o, null, null]);
12948             return null;
12949         }
12950     },
12951
12952     /**
12953      * Determine whether this object has a request outstanding.
12954      * @param {Number} transactionId (Optional) defaults to the last transaction
12955      * @return {Boolean} True if there is an outstanding request.
12956      */
12957     isLoading : function(transId){
12958         if(transId){
12959             return Roo.lib.Ajax.isCallInProgress(transId);
12960         }else{
12961             return this.transId ? true : false;
12962         }
12963     },
12964
12965     /**
12966      * Aborts any outstanding request.
12967      * @param {Number} transactionId (Optional) defaults to the last transaction
12968      */
12969     abort : function(transId){
12970         if(transId || this.isLoading()){
12971             Roo.lib.Ajax.abort(transId || this.transId);
12972         }
12973     },
12974
12975     // private
12976     handleResponse : function(response){
12977         this.transId = false;
12978         var options = response.argument.options;
12979         response.argument = options ? options.argument : null;
12980         this.fireEvent("requestcomplete", this, response, options);
12981         Roo.callback(options.success, options.scope, [response, options]);
12982         Roo.callback(options.callback, options.scope, [options, true, response]);
12983     },
12984
12985     // private
12986     handleFailure : function(response, e){
12987         this.transId = false;
12988         var options = response.argument.options;
12989         response.argument = options ? options.argument : null;
12990         this.fireEvent("requestexception", this, response, options, e);
12991         Roo.callback(options.failure, options.scope, [response, options]);
12992         Roo.callback(options.callback, options.scope, [options, false, response]);
12993     },
12994
12995     // private
12996     doFormUpload : function(o, ps, url){
12997         var id = Roo.id();
12998         var frame = document.createElement('iframe');
12999         frame.id = id;
13000         frame.name = id;
13001         frame.className = 'x-hidden';
13002         if(Roo.isIE){
13003             frame.src = Roo.SSL_SECURE_URL;
13004         }
13005         document.body.appendChild(frame);
13006
13007         if(Roo.isIE){
13008            document.frames[id].name = id;
13009         }
13010
13011         var form = Roo.getDom(o.form);
13012         form.target = id;
13013         form.method = 'POST';
13014         form.enctype = form.encoding = 'multipart/form-data';
13015         if(url){
13016             form.action = url;
13017         }
13018
13019         var hiddens, hd;
13020         if(ps){ // add dynamic params
13021             hiddens = [];
13022             ps = Roo.urlDecode(ps, false);
13023             for(var k in ps){
13024                 if(ps.hasOwnProperty(k)){
13025                     hd = document.createElement('input');
13026                     hd.type = 'hidden';
13027                     hd.name = k;
13028                     hd.value = ps[k];
13029                     form.appendChild(hd);
13030                     hiddens.push(hd);
13031                 }
13032             }
13033         }
13034
13035         function cb(){
13036             var r = {  // bogus response object
13037                 responseText : '',
13038                 responseXML : null
13039             };
13040
13041             r.argument = o ? o.argument : null;
13042
13043             try { //
13044                 var doc;
13045                 if(Roo.isIE){
13046                     doc = frame.contentWindow.document;
13047                 }else {
13048                     doc = (frame.contentDocument || window.frames[id].document);
13049                 }
13050                 if(doc && doc.body){
13051                     r.responseText = doc.body.innerHTML;
13052                 }
13053                 if(doc && doc.XMLDocument){
13054                     r.responseXML = doc.XMLDocument;
13055                 }else {
13056                     r.responseXML = doc;
13057                 }
13058             }
13059             catch(e) {
13060                 // ignore
13061             }
13062
13063             Roo.EventManager.removeListener(frame, 'load', cb, this);
13064
13065             this.fireEvent("requestcomplete", this, r, o);
13066             Roo.callback(o.success, o.scope, [r, o]);
13067             Roo.callback(o.callback, o.scope, [o, true, r]);
13068
13069             setTimeout(function(){document.body.removeChild(frame);}, 100);
13070         }
13071
13072         Roo.EventManager.on(frame, 'load', cb, this);
13073         form.submit();
13074
13075         if(hiddens){ // remove dynamic params
13076             for(var i = 0, len = hiddens.length; i < len; i++){
13077                 form.removeChild(hiddens[i]);
13078             }
13079         }
13080     },
13081     // this is a 'formdata version???'
13082     
13083     
13084     doFormDataUpload : function(o,  url)
13085     {
13086         var formData;
13087         if (o.form) {
13088             var form =  Roo.getDom(o.form);
13089             form.enctype = form.encoding = 'multipart/form-data';
13090             formData = o.formData === true ? new FormData(form) : o.formData;
13091         } else {
13092             formData = o.formData === true ? new FormData() : o.formData;
13093         }
13094         
13095       
13096         var cb = {
13097             success: this.handleResponse,
13098             failure: this.handleFailure,
13099             scope: this,
13100             argument: {options: o},
13101             timeout : o.timeout || this.timeout
13102         };
13103  
13104         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13105             if(o.autoAbort){
13106                 this.abort();
13107             }
13108         }else if(this.autoAbort !== false){
13109             this.abort();
13110         }
13111
13112         //Roo.lib.Ajax.defaultPostHeader = null;
13113         Roo.lib.Ajax.useDefaultHeader = false;
13114         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
13115         Roo.lib.Ajax.useDefaultHeader = true;
13116  
13117          
13118     }
13119     
13120 });
13121 /*
13122  * Based on:
13123  * Ext JS Library 1.1.1
13124  * Copyright(c) 2006-2007, Ext JS, LLC.
13125  *
13126  * Originally Released Under LGPL - original licence link has changed is not relivant.
13127  *
13128  * Fork - LGPL
13129  * <script type="text/javascript">
13130  */
13131  
13132 /**
13133  * Global Ajax request class.
13134  * 
13135  * @class Roo.Ajax
13136  * @extends Roo.data.Connection
13137  * @static
13138  * 
13139  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
13140  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
13141  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
13142  * @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)
13143  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13144  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
13145  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
13146  */
13147 Roo.Ajax = new Roo.data.Connection({
13148     // fix up the docs
13149     /**
13150      * @scope Roo.Ajax
13151      * @type {Boolear} 
13152      */
13153     autoAbort : false,
13154
13155     /**
13156      * Serialize the passed form into a url encoded string
13157      * @scope Roo.Ajax
13158      * @param {String/HTMLElement} form
13159      * @return {String}
13160      */
13161     serializeForm : function(form){
13162         return Roo.lib.Ajax.serializeForm(form);
13163     }
13164 });/*
13165  * Based on:
13166  * Ext JS Library 1.1.1
13167  * Copyright(c) 2006-2007, Ext JS, LLC.
13168  *
13169  * Originally Released Under LGPL - original licence link has changed is not relivant.
13170  *
13171  * Fork - LGPL
13172  * <script type="text/javascript">
13173  */
13174
13175  
13176 /**
13177  * @class Roo.UpdateManager
13178  * @extends Roo.util.Observable
13179  * Provides AJAX-style update for Element object.<br><br>
13180  * Usage:<br>
13181  * <pre><code>
13182  * // Get it from a Roo.Element object
13183  * var el = Roo.get("foo");
13184  * var mgr = el.getUpdateManager();
13185  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
13186  * ...
13187  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
13188  * <br>
13189  * // or directly (returns the same UpdateManager instance)
13190  * var mgr = new Roo.UpdateManager("myElementId");
13191  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
13192  * mgr.on("update", myFcnNeedsToKnow);
13193  * <br>
13194    // short handed call directly from the element object
13195    Roo.get("foo").load({
13196         url: "bar.php",
13197         scripts:true,
13198         params: "for=bar",
13199         text: "Loading Foo..."
13200    });
13201  * </code></pre>
13202  * @constructor
13203  * Create new UpdateManager directly.
13204  * @param {String/HTMLElement/Roo.Element} el The element to update
13205  * @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).
13206  */
13207 Roo.UpdateManager = function(el, forceNew){
13208     el = Roo.get(el);
13209     if(!forceNew && el.updateManager){
13210         return el.updateManager;
13211     }
13212     /**
13213      * The Element object
13214      * @type Roo.Element
13215      */
13216     this.el = el;
13217     /**
13218      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
13219      * @type String
13220      */
13221     this.defaultUrl = null;
13222
13223     this.addEvents({
13224         /**
13225          * @event beforeupdate
13226          * Fired before an update is made, return false from your handler and the update is cancelled.
13227          * @param {Roo.Element} el
13228          * @param {String/Object/Function} url
13229          * @param {String/Object} params
13230          */
13231         "beforeupdate": true,
13232         /**
13233          * @event update
13234          * Fired after successful update is made.
13235          * @param {Roo.Element} el
13236          * @param {Object} oResponseObject The response Object
13237          */
13238         "update": true,
13239         /**
13240          * @event failure
13241          * Fired on update failure.
13242          * @param {Roo.Element} el
13243          * @param {Object} oResponseObject The response Object
13244          */
13245         "failure": true
13246     });
13247     var d = Roo.UpdateManager.defaults;
13248     /**
13249      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
13250      * @type String
13251      */
13252     this.sslBlankUrl = d.sslBlankUrl;
13253     /**
13254      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
13255      * @type Boolean
13256      */
13257     this.disableCaching = d.disableCaching;
13258     /**
13259      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13260      * @type String
13261      */
13262     this.indicatorText = d.indicatorText;
13263     /**
13264      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
13265      * @type String
13266      */
13267     this.showLoadIndicator = d.showLoadIndicator;
13268     /**
13269      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
13270      * @type Number
13271      */
13272     this.timeout = d.timeout;
13273
13274     /**
13275      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
13276      * @type Boolean
13277      */
13278     this.loadScripts = d.loadScripts;
13279
13280     /**
13281      * Transaction object of current executing transaction
13282      */
13283     this.transaction = null;
13284
13285     /**
13286      * @private
13287      */
13288     this.autoRefreshProcId = null;
13289     /**
13290      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
13291      * @type Function
13292      */
13293     this.refreshDelegate = this.refresh.createDelegate(this);
13294     /**
13295      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
13296      * @type Function
13297      */
13298     this.updateDelegate = this.update.createDelegate(this);
13299     /**
13300      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
13301      * @type Function
13302      */
13303     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
13304     /**
13305      * @private
13306      */
13307     this.successDelegate = this.processSuccess.createDelegate(this);
13308     /**
13309      * @private
13310      */
13311     this.failureDelegate = this.processFailure.createDelegate(this);
13312
13313     if(!this.renderer){
13314      /**
13315       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
13316       */
13317     this.renderer = new Roo.UpdateManager.BasicRenderer();
13318     }
13319     
13320     Roo.UpdateManager.superclass.constructor.call(this);
13321 };
13322
13323 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13324     /**
13325      * Get the Element this UpdateManager is bound to
13326      * @return {Roo.Element} The element
13327      */
13328     getEl : function(){
13329         return this.el;
13330     },
13331     /**
13332      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13333      * @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:
13334 <pre><code>
13335 um.update({<br/>
13336     url: "your-url.php",<br/>
13337     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13338     callback: yourFunction,<br/>
13339     scope: yourObject, //(optional scope)  <br/>
13340     discardUrl: false, <br/>
13341     nocache: false,<br/>
13342     text: "Loading...",<br/>
13343     timeout: 30,<br/>
13344     scripts: false<br/>
13345 });
13346 </code></pre>
13347      * The only required property is url. The optional properties nocache, text and scripts
13348      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13349      * @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}
13350      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13351      * @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.
13352      */
13353     update : function(url, params, callback, discardUrl){
13354         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13355             var method = this.method,
13356                 cfg;
13357             if(typeof url == "object"){ // must be config object
13358                 cfg = url;
13359                 url = cfg.url;
13360                 params = params || cfg.params;
13361                 callback = callback || cfg.callback;
13362                 discardUrl = discardUrl || cfg.discardUrl;
13363                 if(callback && cfg.scope){
13364                     callback = callback.createDelegate(cfg.scope);
13365                 }
13366                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13367                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13368                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13369                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13370                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13371             }
13372             this.showLoading();
13373             if(!discardUrl){
13374                 this.defaultUrl = url;
13375             }
13376             if(typeof url == "function"){
13377                 url = url.call(this);
13378             }
13379
13380             method = method || (params ? "POST" : "GET");
13381             if(method == "GET"){
13382                 url = this.prepareUrl(url);
13383             }
13384
13385             var o = Roo.apply(cfg ||{}, {
13386                 url : url,
13387                 params: params,
13388                 success: this.successDelegate,
13389                 failure: this.failureDelegate,
13390                 callback: undefined,
13391                 timeout: (this.timeout*1000),
13392                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13393             });
13394             Roo.log("updated manager called with timeout of " + o.timeout);
13395             this.transaction = Roo.Ajax.request(o);
13396         }
13397     },
13398
13399     /**
13400      * 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.
13401      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13402      * @param {String/HTMLElement} form The form Id or form element
13403      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13404      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13405      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13406      */
13407     formUpdate : function(form, url, reset, callback){
13408         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13409             if(typeof url == "function"){
13410                 url = url.call(this);
13411             }
13412             form = Roo.getDom(form);
13413             this.transaction = Roo.Ajax.request({
13414                 form: form,
13415                 url:url,
13416                 success: this.successDelegate,
13417                 failure: this.failureDelegate,
13418                 timeout: (this.timeout*1000),
13419                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13420             });
13421             this.showLoading.defer(1, this);
13422         }
13423     },
13424
13425     /**
13426      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13427      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13428      */
13429     refresh : function(callback){
13430         if(this.defaultUrl == null){
13431             return;
13432         }
13433         this.update(this.defaultUrl, null, callback, true);
13434     },
13435
13436     /**
13437      * Set this element to auto refresh.
13438      * @param {Number} interval How often to update (in seconds).
13439      * @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)
13440      * @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}
13441      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13442      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13443      */
13444     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13445         if(refreshNow){
13446             this.update(url || this.defaultUrl, params, callback, true);
13447         }
13448         if(this.autoRefreshProcId){
13449             clearInterval(this.autoRefreshProcId);
13450         }
13451         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13452     },
13453
13454     /**
13455      * Stop auto refresh on this element.
13456      */
13457      stopAutoRefresh : function(){
13458         if(this.autoRefreshProcId){
13459             clearInterval(this.autoRefreshProcId);
13460             delete this.autoRefreshProcId;
13461         }
13462     },
13463
13464     isAutoRefreshing : function(){
13465        return this.autoRefreshProcId ? true : false;
13466     },
13467     /**
13468      * Called to update the element to "Loading" state. Override to perform custom action.
13469      */
13470     showLoading : function(){
13471         if(this.showLoadIndicator){
13472             this.el.update(this.indicatorText);
13473         }
13474     },
13475
13476     /**
13477      * Adds unique parameter to query string if disableCaching = true
13478      * @private
13479      */
13480     prepareUrl : function(url){
13481         if(this.disableCaching){
13482             var append = "_dc=" + (new Date().getTime());
13483             if(url.indexOf("?") !== -1){
13484                 url += "&" + append;
13485             }else{
13486                 url += "?" + append;
13487             }
13488         }
13489         return url;
13490     },
13491
13492     /**
13493      * @private
13494      */
13495     processSuccess : function(response){
13496         this.transaction = null;
13497         if(response.argument.form && response.argument.reset){
13498             try{ // put in try/catch since some older FF releases had problems with this
13499                 response.argument.form.reset();
13500             }catch(e){}
13501         }
13502         if(this.loadScripts){
13503             this.renderer.render(this.el, response, this,
13504                 this.updateComplete.createDelegate(this, [response]));
13505         }else{
13506             this.renderer.render(this.el, response, this);
13507             this.updateComplete(response);
13508         }
13509     },
13510
13511     updateComplete : function(response){
13512         this.fireEvent("update", this.el, response);
13513         if(typeof response.argument.callback == "function"){
13514             response.argument.callback(this.el, true, response);
13515         }
13516     },
13517
13518     /**
13519      * @private
13520      */
13521     processFailure : function(response){
13522         this.transaction = null;
13523         this.fireEvent("failure", this.el, response);
13524         if(typeof response.argument.callback == "function"){
13525             response.argument.callback(this.el, false, response);
13526         }
13527     },
13528
13529     /**
13530      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13531      * @param {Object} renderer The object implementing the render() method
13532      */
13533     setRenderer : function(renderer){
13534         this.renderer = renderer;
13535     },
13536
13537     getRenderer : function(){
13538        return this.renderer;
13539     },
13540
13541     /**
13542      * Set the defaultUrl used for updates
13543      * @param {String/Function} defaultUrl The url or a function to call to get the url
13544      */
13545     setDefaultUrl : function(defaultUrl){
13546         this.defaultUrl = defaultUrl;
13547     },
13548
13549     /**
13550      * Aborts the executing transaction
13551      */
13552     abort : function(){
13553         if(this.transaction){
13554             Roo.Ajax.abort(this.transaction);
13555         }
13556     },
13557
13558     /**
13559      * Returns true if an update is in progress
13560      * @return {Boolean}
13561      */
13562     isUpdating : function(){
13563         if(this.transaction){
13564             return Roo.Ajax.isLoading(this.transaction);
13565         }
13566         return false;
13567     }
13568 });
13569
13570 /**
13571  * @class Roo.UpdateManager.defaults
13572  * @static (not really - but it helps the doc tool)
13573  * The defaults collection enables customizing the default properties of UpdateManager
13574  */
13575    Roo.UpdateManager.defaults = {
13576        /**
13577          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13578          * @type Number
13579          */
13580          timeout : 30,
13581
13582          /**
13583          * True to process scripts by default (Defaults to false).
13584          * @type Boolean
13585          */
13586         loadScripts : false,
13587
13588         /**
13589         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13590         * @type String
13591         */
13592         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13593         /**
13594          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13595          * @type Boolean
13596          */
13597         disableCaching : false,
13598         /**
13599          * Whether to show indicatorText when loading (Defaults to true).
13600          * @type Boolean
13601          */
13602         showLoadIndicator : true,
13603         /**
13604          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13605          * @type String
13606          */
13607         indicatorText : '<div class="loading-indicator">Loading...</div>'
13608    };
13609
13610 /**
13611  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13612  *Usage:
13613  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13614  * @param {String/HTMLElement/Roo.Element} el The element to update
13615  * @param {String} url The url
13616  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13617  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13618  * @static
13619  * @deprecated
13620  * @member Roo.UpdateManager
13621  */
13622 Roo.UpdateManager.updateElement = function(el, url, params, options){
13623     var um = Roo.get(el, true).getUpdateManager();
13624     Roo.apply(um, options);
13625     um.update(url, params, options ? options.callback : null);
13626 };
13627 // alias for backwards compat
13628 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13629 /**
13630  * @class Roo.UpdateManager.BasicRenderer
13631  * Default Content renderer. Updates the elements innerHTML with the responseText.
13632  */
13633 Roo.UpdateManager.BasicRenderer = function(){};
13634
13635 Roo.UpdateManager.BasicRenderer.prototype = {
13636     /**
13637      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13638      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13639      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13640      * @param {Roo.Element} el The element being rendered
13641      * @param {Object} response The YUI Connect response object
13642      * @param {UpdateManager} updateManager The calling update manager
13643      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13644      */
13645      render : function(el, response, updateManager, callback){
13646         el.update(response.responseText, updateManager.loadScripts, callback);
13647     }
13648 };
13649 /*
13650  * Based on:
13651  * Roo JS
13652  * (c)) Alan Knowles
13653  * Licence : LGPL
13654  */
13655
13656
13657 /**
13658  * @class Roo.DomTemplate
13659  * @extends Roo.Template
13660  * An effort at a dom based template engine..
13661  *
13662  * Similar to XTemplate, except it uses dom parsing to create the template..
13663  *
13664  * Supported features:
13665  *
13666  *  Tags:
13667
13668 <pre><code>
13669       {a_variable} - output encoded.
13670       {a_variable.format:("Y-m-d")} - call a method on the variable
13671       {a_variable:raw} - unencoded output
13672       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13673       {a_variable:this.method_on_template(...)} - call a method on the template object.
13674  
13675 </code></pre>
13676  *  The tpl tag:
13677 <pre><code>
13678         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13679         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13680         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13681         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13682   
13683 </code></pre>
13684  *      
13685  */
13686 Roo.DomTemplate = function()
13687 {
13688      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13689      if (this.html) {
13690         this.compile();
13691      }
13692 };
13693
13694
13695 Roo.extend(Roo.DomTemplate, Roo.Template, {
13696     /**
13697      * id counter for sub templates.
13698      */
13699     id : 0,
13700     /**
13701      * flag to indicate if dom parser is inside a pre,
13702      * it will strip whitespace if not.
13703      */
13704     inPre : false,
13705     
13706     /**
13707      * The various sub templates
13708      */
13709     tpls : false,
13710     
13711     
13712     
13713     /**
13714      *
13715      * basic tag replacing syntax
13716      * WORD:WORD()
13717      *
13718      * // you can fake an object call by doing this
13719      *  x.t:(test,tesT) 
13720      * 
13721      */
13722     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13723     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13724     
13725     iterChild : function (node, method) {
13726         
13727         var oldPre = this.inPre;
13728         if (node.tagName == 'PRE') {
13729             this.inPre = true;
13730         }
13731         for( var i = 0; i < node.childNodes.length; i++) {
13732             method.call(this, node.childNodes[i]);
13733         }
13734         this.inPre = oldPre;
13735     },
13736     
13737     
13738     
13739     /**
13740      * compile the template
13741      *
13742      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13743      *
13744      */
13745     compile: function()
13746     {
13747         var s = this.html;
13748         
13749         // covert the html into DOM...
13750         var doc = false;
13751         var div =false;
13752         try {
13753             doc = document.implementation.createHTMLDocument("");
13754             doc.documentElement.innerHTML =   this.html  ;
13755             div = doc.documentElement;
13756         } catch (e) {
13757             // old IE... - nasty -- it causes all sorts of issues.. with
13758             // images getting pulled from server..
13759             div = document.createElement('div');
13760             div.innerHTML = this.html;
13761         }
13762         //doc.documentElement.innerHTML = htmlBody
13763          
13764         
13765         
13766         this.tpls = [];
13767         var _t = this;
13768         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13769         
13770         var tpls = this.tpls;
13771         
13772         // create a top level template from the snippet..
13773         
13774         //Roo.log(div.innerHTML);
13775         
13776         var tpl = {
13777             uid : 'master',
13778             id : this.id++,
13779             attr : false,
13780             value : false,
13781             body : div.innerHTML,
13782             
13783             forCall : false,
13784             execCall : false,
13785             dom : div,
13786             isTop : true
13787             
13788         };
13789         tpls.unshift(tpl);
13790         
13791         
13792         // compile them...
13793         this.tpls = [];
13794         Roo.each(tpls, function(tp){
13795             this.compileTpl(tp);
13796             this.tpls[tp.id] = tp;
13797         }, this);
13798         
13799         this.master = tpls[0];
13800         return this;
13801         
13802         
13803     },
13804     
13805     compileNode : function(node, istop) {
13806         // test for
13807         //Roo.log(node);
13808         
13809         
13810         // skip anything not a tag..
13811         if (node.nodeType != 1) {
13812             if (node.nodeType == 3 && !this.inPre) {
13813                 // reduce white space..
13814                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13815                 
13816             }
13817             return;
13818         }
13819         
13820         var tpl = {
13821             uid : false,
13822             id : false,
13823             attr : false,
13824             value : false,
13825             body : '',
13826             
13827             forCall : false,
13828             execCall : false,
13829             dom : false,
13830             isTop : istop
13831             
13832             
13833         };
13834         
13835         
13836         switch(true) {
13837             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13838             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13839             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13840             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13841             // no default..
13842         }
13843         
13844         
13845         if (!tpl.attr) {
13846             // just itterate children..
13847             this.iterChild(node,this.compileNode);
13848             return;
13849         }
13850         tpl.uid = this.id++;
13851         tpl.value = node.getAttribute('roo-' +  tpl.attr);
13852         node.removeAttribute('roo-'+ tpl.attr);
13853         if (tpl.attr != 'name') {
13854             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13855             node.parentNode.replaceChild(placeholder,  node);
13856         } else {
13857             
13858             var placeholder =  document.createElement('span');
13859             placeholder.className = 'roo-tpl-' + tpl.value;
13860             node.parentNode.replaceChild(placeholder,  node);
13861         }
13862         
13863         // parent now sees '{domtplXXXX}
13864         this.iterChild(node,this.compileNode);
13865         
13866         // we should now have node body...
13867         var div = document.createElement('div');
13868         div.appendChild(node);
13869         tpl.dom = node;
13870         // this has the unfortunate side effect of converting tagged attributes
13871         // eg. href="{...}" into %7C...%7D
13872         // this has been fixed by searching for those combo's although it's a bit hacky..
13873         
13874         
13875         tpl.body = div.innerHTML;
13876         
13877         
13878          
13879         tpl.id = tpl.uid;
13880         switch(tpl.attr) {
13881             case 'for' :
13882                 switch (tpl.value) {
13883                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13884                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13885                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13886                 }
13887                 break;
13888             
13889             case 'exec':
13890                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13891                 break;
13892             
13893             case 'if':     
13894                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13895                 break;
13896             
13897             case 'name':
13898                 tpl.id  = tpl.value; // replace non characters???
13899                 break;
13900             
13901         }
13902         
13903         
13904         this.tpls.push(tpl);
13905         
13906         
13907         
13908     },
13909     
13910     
13911     
13912     
13913     /**
13914      * Compile a segment of the template into a 'sub-template'
13915      *
13916      * 
13917      * 
13918      *
13919      */
13920     compileTpl : function(tpl)
13921     {
13922         var fm = Roo.util.Format;
13923         var useF = this.disableFormats !== true;
13924         
13925         var sep = Roo.isGecko ? "+\n" : ",\n";
13926         
13927         var undef = function(str) {
13928             Roo.debug && Roo.log("Property not found :"  + str);
13929             return '';
13930         };
13931           
13932         //Roo.log(tpl.body);
13933         
13934         
13935         
13936         var fn = function(m, lbrace, name, format, args)
13937         {
13938             //Roo.log("ARGS");
13939             //Roo.log(arguments);
13940             args = args ? args.replace(/\\'/g,"'") : args;
13941             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13942             if (typeof(format) == 'undefined') {
13943                 format =  'htmlEncode'; 
13944             }
13945             if (format == 'raw' ) {
13946                 format = false;
13947             }
13948             
13949             if(name.substr(0, 6) == 'domtpl'){
13950                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13951             }
13952             
13953             // build an array of options to determine if value is undefined..
13954             
13955             // basically get 'xxxx.yyyy' then do
13956             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13957             //    (function () { Roo.log("Property not found"); return ''; })() :
13958             //    ......
13959             
13960             var udef_ar = [];
13961             var lookfor = '';
13962             Roo.each(name.split('.'), function(st) {
13963                 lookfor += (lookfor.length ? '.': '') + st;
13964                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
13965             });
13966             
13967             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13968             
13969             
13970             if(format && useF){
13971                 
13972                 args = args ? ',' + args : "";
13973                  
13974                 if(format.substr(0, 5) != "this."){
13975                     format = "fm." + format + '(';
13976                 }else{
13977                     format = 'this.call("'+ format.substr(5) + '", ';
13978                     args = ", values";
13979                 }
13980                 
13981                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
13982             }
13983              
13984             if (args && args.length) {
13985                 // called with xxyx.yuu:(test,test)
13986                 // change to ()
13987                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
13988             }
13989             // raw.. - :raw modifier..
13990             return "'"+ sep + udef_st  + name + ")"+sep+"'";
13991             
13992         };
13993         var body;
13994         // branched to use + in gecko and [].join() in others
13995         if(Roo.isGecko){
13996             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
13997                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13998                     "';};};";
13999         }else{
14000             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
14001             body.push(tpl.body.replace(/(\r\n|\n)/g,
14002                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
14003             body.push("'].join('');};};");
14004             body = body.join('');
14005         }
14006         
14007         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
14008        
14009         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
14010         eval(body);
14011         
14012         return this;
14013     },
14014      
14015     /**
14016      * same as applyTemplate, except it's done to one of the subTemplates
14017      * when using named templates, you can do:
14018      *
14019      * var str = pl.applySubTemplate('your-name', values);
14020      *
14021      * 
14022      * @param {Number} id of the template
14023      * @param {Object} values to apply to template
14024      * @param {Object} parent (normaly the instance of this object)
14025      */
14026     applySubTemplate : function(id, values, parent)
14027     {
14028         
14029         
14030         var t = this.tpls[id];
14031         
14032         
14033         try { 
14034             if(t.ifCall && !t.ifCall.call(this, values, parent)){
14035                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
14036                 return '';
14037             }
14038         } catch(e) {
14039             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
14040             Roo.log(values);
14041           
14042             return '';
14043         }
14044         try { 
14045             
14046             if(t.execCall && t.execCall.call(this, values, parent)){
14047                 return '';
14048             }
14049         } catch(e) {
14050             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14051             Roo.log(values);
14052             return '';
14053         }
14054         
14055         try {
14056             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
14057             parent = t.target ? values : parent;
14058             if(t.forCall && vs instanceof Array){
14059                 var buf = [];
14060                 for(var i = 0, len = vs.length; i < len; i++){
14061                     try {
14062                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
14063                     } catch (e) {
14064                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14065                         Roo.log(e.body);
14066                         //Roo.log(t.compiled);
14067                         Roo.log(vs[i]);
14068                     }   
14069                 }
14070                 return buf.join('');
14071             }
14072         } catch (e) {
14073             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14074             Roo.log(values);
14075             return '';
14076         }
14077         try {
14078             return t.compiled.call(this, vs, parent);
14079         } catch (e) {
14080             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14081             Roo.log(e.body);
14082             //Roo.log(t.compiled);
14083             Roo.log(values);
14084             return '';
14085         }
14086     },
14087
14088    
14089
14090     applyTemplate : function(values){
14091         return this.master.compiled.call(this, values, {});
14092         //var s = this.subs;
14093     },
14094
14095     apply : function(){
14096         return this.applyTemplate.apply(this, arguments);
14097     }
14098
14099  });
14100
14101 Roo.DomTemplate.from = function(el){
14102     el = Roo.getDom(el);
14103     return new Roo.Domtemplate(el.value || el.innerHTML);
14104 };/*
14105  * Based on:
14106  * Ext JS Library 1.1.1
14107  * Copyright(c) 2006-2007, Ext JS, LLC.
14108  *
14109  * Originally Released Under LGPL - original licence link has changed is not relivant.
14110  *
14111  * Fork - LGPL
14112  * <script type="text/javascript">
14113  */
14114
14115 /**
14116  * @class Roo.util.DelayedTask
14117  * Provides a convenient method of performing setTimeout where a new
14118  * timeout cancels the old timeout. An example would be performing validation on a keypress.
14119  * You can use this class to buffer
14120  * the keypress events for a certain number of milliseconds, and perform only if they stop
14121  * for that amount of time.
14122  * @constructor The parameters to this constructor serve as defaults and are not required.
14123  * @param {Function} fn (optional) The default function to timeout
14124  * @param {Object} scope (optional) The default scope of that timeout
14125  * @param {Array} args (optional) The default Array of arguments
14126  */
14127 Roo.util.DelayedTask = function(fn, scope, args){
14128     var id = null, d, t;
14129
14130     var call = function(){
14131         var now = new Date().getTime();
14132         if(now - t >= d){
14133             clearInterval(id);
14134             id = null;
14135             fn.apply(scope, args || []);
14136         }
14137     };
14138     /**
14139      * Cancels any pending timeout and queues a new one
14140      * @param {Number} delay The milliseconds to delay
14141      * @param {Function} newFn (optional) Overrides function passed to constructor
14142      * @param {Object} newScope (optional) Overrides scope passed to constructor
14143      * @param {Array} newArgs (optional) Overrides args passed to constructor
14144      */
14145     this.delay = function(delay, newFn, newScope, newArgs){
14146         if(id && delay != d){
14147             this.cancel();
14148         }
14149         d = delay;
14150         t = new Date().getTime();
14151         fn = newFn || fn;
14152         scope = newScope || scope;
14153         args = newArgs || args;
14154         if(!id){
14155             id = setInterval(call, d);
14156         }
14157     };
14158
14159     /**
14160      * Cancel the last queued timeout
14161      */
14162     this.cancel = function(){
14163         if(id){
14164             clearInterval(id);
14165             id = null;
14166         }
14167     };
14168 };/*
14169  * Based on:
14170  * Ext JS Library 1.1.1
14171  * Copyright(c) 2006-2007, Ext JS, LLC.
14172  *
14173  * Originally Released Under LGPL - original licence link has changed is not relivant.
14174  *
14175  * Fork - LGPL
14176  * <script type="text/javascript">
14177  */
14178 /**
14179  * @class Roo.util.TaskRunner
14180  * Manage background tasks - not sure why this is better that setInterval?
14181  * @static
14182  *
14183  */
14184  
14185 Roo.util.TaskRunner = function(interval){
14186     interval = interval || 10;
14187     var tasks = [], removeQueue = [];
14188     var id = 0;
14189     var running = false;
14190
14191     var stopThread = function(){
14192         running = false;
14193         clearInterval(id);
14194         id = 0;
14195     };
14196
14197     var startThread = function(){
14198         if(!running){
14199             running = true;
14200             id = setInterval(runTasks, interval);
14201         }
14202     };
14203
14204     var removeTask = function(task){
14205         removeQueue.push(task);
14206         if(task.onStop){
14207             task.onStop();
14208         }
14209     };
14210
14211     var runTasks = function(){
14212         if(removeQueue.length > 0){
14213             for(var i = 0, len = removeQueue.length; i < len; i++){
14214                 tasks.remove(removeQueue[i]);
14215             }
14216             removeQueue = [];
14217             if(tasks.length < 1){
14218                 stopThread();
14219                 return;
14220             }
14221         }
14222         var now = new Date().getTime();
14223         for(var i = 0, len = tasks.length; i < len; ++i){
14224             var t = tasks[i];
14225             var itime = now - t.taskRunTime;
14226             if(t.interval <= itime){
14227                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
14228                 t.taskRunTime = now;
14229                 if(rt === false || t.taskRunCount === t.repeat){
14230                     removeTask(t);
14231                     return;
14232                 }
14233             }
14234             if(t.duration && t.duration <= (now - t.taskStartTime)){
14235                 removeTask(t);
14236             }
14237         }
14238     };
14239
14240     /**
14241      * Queues a new task.
14242      * @param {Object} task
14243      *
14244      * Task property : interval = how frequent to run.
14245      * Task object should implement
14246      * function run()
14247      * Task object may implement
14248      * function onStop()
14249      */
14250     this.start = function(task){
14251         tasks.push(task);
14252         task.taskStartTime = new Date().getTime();
14253         task.taskRunTime = 0;
14254         task.taskRunCount = 0;
14255         startThread();
14256         return task;
14257     };
14258     /**
14259      * Stop  new task.
14260      * @param {Object} task
14261      */
14262     this.stop = function(task){
14263         removeTask(task);
14264         return task;
14265     };
14266     /**
14267      * Stop all Tasks
14268      */
14269     this.stopAll = function(){
14270         stopThread();
14271         for(var i = 0, len = tasks.length; i < len; i++){
14272             if(tasks[i].onStop){
14273                 tasks[i].onStop();
14274             }
14275         }
14276         tasks = [];
14277         removeQueue = [];
14278     };
14279 };
14280
14281 Roo.TaskMgr = new Roo.util.TaskRunner();/*
14282  * Based on:
14283  * Ext JS Library 1.1.1
14284  * Copyright(c) 2006-2007, Ext JS, LLC.
14285  *
14286  * Originally Released Under LGPL - original licence link has changed is not relivant.
14287  *
14288  * Fork - LGPL
14289  * <script type="text/javascript">
14290  */
14291
14292  
14293 /**
14294  * @class Roo.util.MixedCollection
14295  * @extends Roo.util.Observable
14296  * A Collection class that maintains both numeric indexes and keys and exposes events.
14297  * @constructor
14298  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
14299  * collection (defaults to false)
14300  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
14301  * and return the key value for that item.  This is used when available to look up the key on items that
14302  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
14303  * equivalent to providing an implementation for the {@link #getKey} method.
14304  */
14305 Roo.util.MixedCollection = function(allowFunctions, keyFn){
14306     this.items = [];
14307     this.map = {};
14308     this.keys = [];
14309     this.length = 0;
14310     this.addEvents({
14311         /**
14312          * @event clear
14313          * Fires when the collection is cleared.
14314          */
14315         "clear" : true,
14316         /**
14317          * @event add
14318          * Fires when an item is added to the collection.
14319          * @param {Number} index The index at which the item was added.
14320          * @param {Object} o The item added.
14321          * @param {String} key The key associated with the added item.
14322          */
14323         "add" : true,
14324         /**
14325          * @event replace
14326          * Fires when an item is replaced in the collection.
14327          * @param {String} key he key associated with the new added.
14328          * @param {Object} old The item being replaced.
14329          * @param {Object} new The new item.
14330          */
14331         "replace" : true,
14332         /**
14333          * @event remove
14334          * Fires when an item is removed from the collection.
14335          * @param {Object} o The item being removed.
14336          * @param {String} key (optional) The key associated with the removed item.
14337          */
14338         "remove" : true,
14339         "sort" : true
14340     });
14341     this.allowFunctions = allowFunctions === true;
14342     if(keyFn){
14343         this.getKey = keyFn;
14344     }
14345     Roo.util.MixedCollection.superclass.constructor.call(this);
14346 };
14347
14348 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14349     allowFunctions : false,
14350     
14351 /**
14352  * Adds an item to the collection.
14353  * @param {String} key The key to associate with the item
14354  * @param {Object} o The item to add.
14355  * @return {Object} The item added.
14356  */
14357     add : function(key, o){
14358         if(arguments.length == 1){
14359             o = arguments[0];
14360             key = this.getKey(o);
14361         }
14362         if(typeof key == "undefined" || key === null){
14363             this.length++;
14364             this.items.push(o);
14365             this.keys.push(null);
14366         }else{
14367             var old = this.map[key];
14368             if(old){
14369                 return this.replace(key, o);
14370             }
14371             this.length++;
14372             this.items.push(o);
14373             this.map[key] = o;
14374             this.keys.push(key);
14375         }
14376         this.fireEvent("add", this.length-1, o, key);
14377         return o;
14378     },
14379        
14380 /**
14381   * MixedCollection has a generic way to fetch keys if you implement getKey.
14382 <pre><code>
14383 // normal way
14384 var mc = new Roo.util.MixedCollection();
14385 mc.add(someEl.dom.id, someEl);
14386 mc.add(otherEl.dom.id, otherEl);
14387 //and so on
14388
14389 // using getKey
14390 var mc = new Roo.util.MixedCollection();
14391 mc.getKey = function(el){
14392    return el.dom.id;
14393 };
14394 mc.add(someEl);
14395 mc.add(otherEl);
14396
14397 // or via the constructor
14398 var mc = new Roo.util.MixedCollection(false, function(el){
14399    return el.dom.id;
14400 });
14401 mc.add(someEl);
14402 mc.add(otherEl);
14403 </code></pre>
14404  * @param o {Object} The item for which to find the key.
14405  * @return {Object} The key for the passed item.
14406  */
14407     getKey : function(o){
14408          return o.id; 
14409     },
14410    
14411 /**
14412  * Replaces an item in the collection.
14413  * @param {String} key The key associated with the item to replace, or the item to replace.
14414  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14415  * @return {Object}  The new item.
14416  */
14417     replace : function(key, o){
14418         if(arguments.length == 1){
14419             o = arguments[0];
14420             key = this.getKey(o);
14421         }
14422         var old = this.item(key);
14423         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14424              return this.add(key, o);
14425         }
14426         var index = this.indexOfKey(key);
14427         this.items[index] = o;
14428         this.map[key] = o;
14429         this.fireEvent("replace", key, old, o);
14430         return o;
14431     },
14432    
14433 /**
14434  * Adds all elements of an Array or an Object to the collection.
14435  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14436  * an Array of values, each of which are added to the collection.
14437  */
14438     addAll : function(objs){
14439         if(arguments.length > 1 || objs instanceof Array){
14440             var args = arguments.length > 1 ? arguments : objs;
14441             for(var i = 0, len = args.length; i < len; i++){
14442                 this.add(args[i]);
14443             }
14444         }else{
14445             for(var key in objs){
14446                 if(this.allowFunctions || typeof objs[key] != "function"){
14447                     this.add(key, objs[key]);
14448                 }
14449             }
14450         }
14451     },
14452    
14453 /**
14454  * Executes the specified function once for every item in the collection, passing each
14455  * item as the first and only parameter. returning false from the function will stop the iteration.
14456  * @param {Function} fn The function to execute for each item.
14457  * @param {Object} scope (optional) The scope in which to execute the function.
14458  */
14459     each : function(fn, scope){
14460         var items = [].concat(this.items); // each safe for removal
14461         for(var i = 0, len = items.length; i < len; i++){
14462             if(fn.call(scope || items[i], items[i], i, len) === false){
14463                 break;
14464             }
14465         }
14466     },
14467    
14468 /**
14469  * Executes the specified function once for every key in the collection, passing each
14470  * key, and its associated item as the first two parameters.
14471  * @param {Function} fn The function to execute for each item.
14472  * @param {Object} scope (optional) The scope in which to execute the function.
14473  */
14474     eachKey : function(fn, scope){
14475         for(var i = 0, len = this.keys.length; i < len; i++){
14476             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14477         }
14478     },
14479    
14480 /**
14481  * Returns the first item in the collection which elicits a true return value from the
14482  * passed selection function.
14483  * @param {Function} fn The selection function to execute for each item.
14484  * @param {Object} scope (optional) The scope in which to execute the function.
14485  * @return {Object} The first item in the collection which returned true from the selection function.
14486  */
14487     find : function(fn, scope){
14488         for(var i = 0, len = this.items.length; i < len; i++){
14489             if(fn.call(scope || window, this.items[i], this.keys[i])){
14490                 return this.items[i];
14491             }
14492         }
14493         return null;
14494     },
14495    
14496 /**
14497  * Inserts an item at the specified index in the collection.
14498  * @param {Number} index The index to insert the item at.
14499  * @param {String} key The key to associate with the new item, or the item itself.
14500  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14501  * @return {Object} The item inserted.
14502  */
14503     insert : function(index, key, o){
14504         if(arguments.length == 2){
14505             o = arguments[1];
14506             key = this.getKey(o);
14507         }
14508         if(index >= this.length){
14509             return this.add(key, o);
14510         }
14511         this.length++;
14512         this.items.splice(index, 0, o);
14513         if(typeof key != "undefined" && key != null){
14514             this.map[key] = o;
14515         }
14516         this.keys.splice(index, 0, key);
14517         this.fireEvent("add", index, o, key);
14518         return o;
14519     },
14520    
14521 /**
14522  * Removed an item from the collection.
14523  * @param {Object} o The item to remove.
14524  * @return {Object} The item removed.
14525  */
14526     remove : function(o){
14527         return this.removeAt(this.indexOf(o));
14528     },
14529    
14530 /**
14531  * Remove an item from a specified index in the collection.
14532  * @param {Number} index The index within the collection of the item to remove.
14533  */
14534     removeAt : function(index){
14535         if(index < this.length && index >= 0){
14536             this.length--;
14537             var o = this.items[index];
14538             this.items.splice(index, 1);
14539             var key = this.keys[index];
14540             if(typeof key != "undefined"){
14541                 delete this.map[key];
14542             }
14543             this.keys.splice(index, 1);
14544             this.fireEvent("remove", o, key);
14545         }
14546     },
14547    
14548 /**
14549  * Removed an item associated with the passed key fom the collection.
14550  * @param {String} key The key of the item to remove.
14551  */
14552     removeKey : function(key){
14553         return this.removeAt(this.indexOfKey(key));
14554     },
14555    
14556 /**
14557  * Returns the number of items in the collection.
14558  * @return {Number} the number of items in the collection.
14559  */
14560     getCount : function(){
14561         return this.length; 
14562     },
14563    
14564 /**
14565  * Returns index within the collection of the passed Object.
14566  * @param {Object} o The item to find the index of.
14567  * @return {Number} index of the item.
14568  */
14569     indexOf : function(o){
14570         if(!this.items.indexOf){
14571             for(var i = 0, len = this.items.length; i < len; i++){
14572                 if(this.items[i] == o) {
14573                     return i;
14574                 }
14575             }
14576             return -1;
14577         }else{
14578             return this.items.indexOf(o);
14579         }
14580     },
14581    
14582 /**
14583  * Returns index within the collection of the passed key.
14584  * @param {String} key The key to find the index of.
14585  * @return {Number} index of the key.
14586  */
14587     indexOfKey : function(key){
14588         if(!this.keys.indexOf){
14589             for(var i = 0, len = this.keys.length; i < len; i++){
14590                 if(this.keys[i] == key) {
14591                     return i;
14592                 }
14593             }
14594             return -1;
14595         }else{
14596             return this.keys.indexOf(key);
14597         }
14598     },
14599    
14600 /**
14601  * Returns the item associated with the passed key OR index. Key has priority over index.
14602  * @param {String/Number} key The key or index of the item.
14603  * @return {Object} The item associated with the passed key.
14604  */
14605     item : function(key){
14606         if (key === 'length') {
14607             return null;
14608         }
14609         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14610         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14611     },
14612     
14613 /**
14614  * Returns the item at the specified index.
14615  * @param {Number} index The index of the item.
14616  * @return {Object}
14617  */
14618     itemAt : function(index){
14619         return this.items[index];
14620     },
14621     
14622 /**
14623  * Returns the item associated with the passed key.
14624  * @param {String/Number} key The key of the item.
14625  * @return {Object} The item associated with the passed key.
14626  */
14627     key : function(key){
14628         return this.map[key];
14629     },
14630    
14631 /**
14632  * Returns true if the collection contains the passed Object as an item.
14633  * @param {Object} o  The Object to look for in the collection.
14634  * @return {Boolean} True if the collection contains the Object as an item.
14635  */
14636     contains : function(o){
14637         return this.indexOf(o) != -1;
14638     },
14639    
14640 /**
14641  * Returns true if the collection contains the passed Object as a key.
14642  * @param {String} key The key to look for in the collection.
14643  * @return {Boolean} True if the collection contains the Object as a key.
14644  */
14645     containsKey : function(key){
14646         return typeof this.map[key] != "undefined";
14647     },
14648    
14649 /**
14650  * Removes all items from the collection.
14651  */
14652     clear : function(){
14653         this.length = 0;
14654         this.items = [];
14655         this.keys = [];
14656         this.map = {};
14657         this.fireEvent("clear");
14658     },
14659    
14660 /**
14661  * Returns the first item in the collection.
14662  * @return {Object} the first item in the collection..
14663  */
14664     first : function(){
14665         return this.items[0]; 
14666     },
14667    
14668 /**
14669  * Returns the last item in the collection.
14670  * @return {Object} the last item in the collection..
14671  */
14672     last : function(){
14673         return this.items[this.length-1];   
14674     },
14675     
14676     _sort : function(property, dir, fn){
14677         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14678         fn = fn || function(a, b){
14679             return a-b;
14680         };
14681         var c = [], k = this.keys, items = this.items;
14682         for(var i = 0, len = items.length; i < len; i++){
14683             c[c.length] = {key: k[i], value: items[i], index: i};
14684         }
14685         c.sort(function(a, b){
14686             var v = fn(a[property], b[property]) * dsc;
14687             if(v == 0){
14688                 v = (a.index < b.index ? -1 : 1);
14689             }
14690             return v;
14691         });
14692         for(var i = 0, len = c.length; i < len; i++){
14693             items[i] = c[i].value;
14694             k[i] = c[i].key;
14695         }
14696         this.fireEvent("sort", this);
14697     },
14698     
14699     /**
14700      * Sorts this collection with the passed comparison function
14701      * @param {String} direction (optional) "ASC" or "DESC"
14702      * @param {Function} fn (optional) comparison function
14703      */
14704     sort : function(dir, fn){
14705         this._sort("value", dir, fn);
14706     },
14707     
14708     /**
14709      * Sorts this collection by keys
14710      * @param {String} direction (optional) "ASC" or "DESC"
14711      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14712      */
14713     keySort : function(dir, fn){
14714         this._sort("key", dir, fn || function(a, b){
14715             return String(a).toUpperCase()-String(b).toUpperCase();
14716         });
14717     },
14718     
14719     /**
14720      * Returns a range of items in this collection
14721      * @param {Number} startIndex (optional) defaults to 0
14722      * @param {Number} endIndex (optional) default to the last item
14723      * @return {Array} An array of items
14724      */
14725     getRange : function(start, end){
14726         var items = this.items;
14727         if(items.length < 1){
14728             return [];
14729         }
14730         start = start || 0;
14731         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14732         var r = [];
14733         if(start <= end){
14734             for(var i = start; i <= end; i++) {
14735                     r[r.length] = items[i];
14736             }
14737         }else{
14738             for(var i = start; i >= end; i--) {
14739                     r[r.length] = items[i];
14740             }
14741         }
14742         return r;
14743     },
14744         
14745     /**
14746      * Filter the <i>objects</i> in this collection by a specific property. 
14747      * Returns a new collection that has been filtered.
14748      * @param {String} property A property on your objects
14749      * @param {String/RegExp} value Either string that the property values 
14750      * should start with or a RegExp to test against the property
14751      * @return {MixedCollection} The new filtered collection
14752      */
14753     filter : function(property, value){
14754         if(!value.exec){ // not a regex
14755             value = String(value);
14756             if(value.length == 0){
14757                 return this.clone();
14758             }
14759             value = new RegExp("^" + Roo.escapeRe(value), "i");
14760         }
14761         return this.filterBy(function(o){
14762             return o && value.test(o[property]);
14763         });
14764         },
14765     
14766     /**
14767      * Filter by a function. * Returns a new collection that has been filtered.
14768      * The passed function will be called with each 
14769      * object in the collection. If the function returns true, the value is included 
14770      * otherwise it is filtered.
14771      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14772      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14773      * @return {MixedCollection} The new filtered collection
14774      */
14775     filterBy : function(fn, scope){
14776         var r = new Roo.util.MixedCollection();
14777         r.getKey = this.getKey;
14778         var k = this.keys, it = this.items;
14779         for(var i = 0, len = it.length; i < len; i++){
14780             if(fn.call(scope||this, it[i], k[i])){
14781                                 r.add(k[i], it[i]);
14782                         }
14783         }
14784         return r;
14785     },
14786     
14787     /**
14788      * Creates a duplicate of this collection
14789      * @return {MixedCollection}
14790      */
14791     clone : function(){
14792         var r = new Roo.util.MixedCollection();
14793         var k = this.keys, it = this.items;
14794         for(var i = 0, len = it.length; i < len; i++){
14795             r.add(k[i], it[i]);
14796         }
14797         r.getKey = this.getKey;
14798         return r;
14799     }
14800 });
14801 /**
14802  * Returns the item associated with the passed key or index.
14803  * @method
14804  * @param {String/Number} key The key or index of the item.
14805  * @return {Object} The item associated with the passed key.
14806  */
14807 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14808  * Based on:
14809  * Ext JS Library 1.1.1
14810  * Copyright(c) 2006-2007, Ext JS, LLC.
14811  *
14812  * Originally Released Under LGPL - original licence link has changed is not relivant.
14813  *
14814  * Fork - LGPL
14815  * <script type="text/javascript">
14816  */
14817 /**
14818  * @class Roo.util.JSON
14819  * Modified version of Douglas Crockford"s json.js that doesn"t
14820  * mess with the Object prototype 
14821  * http://www.json.org/js.html
14822  * @static
14823  */
14824 Roo.util.JSON = new (function(){
14825     var useHasOwn = {}.hasOwnProperty ? true : false;
14826     
14827     // crashes Safari in some instances
14828     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14829     
14830     var pad = function(n) {
14831         return n < 10 ? "0" + n : n;
14832     };
14833     
14834     var m = {
14835         "\b": '\\b',
14836         "\t": '\\t',
14837         "\n": '\\n',
14838         "\f": '\\f',
14839         "\r": '\\r',
14840         '"' : '\\"',
14841         "\\": '\\\\'
14842     };
14843
14844     var encodeString = function(s){
14845         if (/["\\\x00-\x1f]/.test(s)) {
14846             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14847                 var c = m[b];
14848                 if(c){
14849                     return c;
14850                 }
14851                 c = b.charCodeAt();
14852                 return "\\u00" +
14853                     Math.floor(c / 16).toString(16) +
14854                     (c % 16).toString(16);
14855             }) + '"';
14856         }
14857         return '"' + s + '"';
14858     };
14859     
14860     var encodeArray = function(o){
14861         var a = ["["], b, i, l = o.length, v;
14862             for (i = 0; i < l; i += 1) {
14863                 v = o[i];
14864                 switch (typeof v) {
14865                     case "undefined":
14866                     case "function":
14867                     case "unknown":
14868                         break;
14869                     default:
14870                         if (b) {
14871                             a.push(',');
14872                         }
14873                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14874                         b = true;
14875                 }
14876             }
14877             a.push("]");
14878             return a.join("");
14879     };
14880     
14881     var encodeDate = function(o){
14882         return '"' + o.getFullYear() + "-" +
14883                 pad(o.getMonth() + 1) + "-" +
14884                 pad(o.getDate()) + "T" +
14885                 pad(o.getHours()) + ":" +
14886                 pad(o.getMinutes()) + ":" +
14887                 pad(o.getSeconds()) + '"';
14888     };
14889     
14890     /**
14891      * Encodes an Object, Array or other value
14892      * @param {Mixed} o The variable to encode
14893      * @return {String} The JSON string
14894      */
14895     this.encode = function(o)
14896     {
14897         // should this be extended to fully wrap stringify..
14898         
14899         if(typeof o == "undefined" || o === null){
14900             return "null";
14901         }else if(o instanceof Array){
14902             return encodeArray(o);
14903         }else if(o instanceof Date){
14904             return encodeDate(o);
14905         }else if(typeof o == "string"){
14906             return encodeString(o);
14907         }else if(typeof o == "number"){
14908             return isFinite(o) ? String(o) : "null";
14909         }else if(typeof o == "boolean"){
14910             return String(o);
14911         }else {
14912             var a = ["{"], b, i, v;
14913             for (i in o) {
14914                 if(!useHasOwn || o.hasOwnProperty(i)) {
14915                     v = o[i];
14916                     switch (typeof v) {
14917                     case "undefined":
14918                     case "function":
14919                     case "unknown":
14920                         break;
14921                     default:
14922                         if(b){
14923                             a.push(',');
14924                         }
14925                         a.push(this.encode(i), ":",
14926                                 v === null ? "null" : this.encode(v));
14927                         b = true;
14928                     }
14929                 }
14930             }
14931             a.push("}");
14932             return a.join("");
14933         }
14934     };
14935     
14936     /**
14937      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14938      * @param {String} json The JSON string
14939      * @return {Object} The resulting object
14940      */
14941     this.decode = function(json){
14942         
14943         return  /** eval:var:json */ eval("(" + json + ')');
14944     };
14945 })();
14946 /** 
14947  * Shorthand for {@link Roo.util.JSON#encode}
14948  * @member Roo encode 
14949  * @method */
14950 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14951 /** 
14952  * Shorthand for {@link Roo.util.JSON#decode}
14953  * @member Roo decode 
14954  * @method */
14955 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14956 /*
14957  * Based on:
14958  * Ext JS Library 1.1.1
14959  * Copyright(c) 2006-2007, Ext JS, LLC.
14960  *
14961  * Originally Released Under LGPL - original licence link has changed is not relivant.
14962  *
14963  * Fork - LGPL
14964  * <script type="text/javascript">
14965  */
14966  
14967 /**
14968  * @class Roo.util.Format
14969  * Reusable data formatting functions
14970  * @static
14971  */
14972 Roo.util.Format = function(){
14973     var trimRe = /^\s+|\s+$/g;
14974     return {
14975         /**
14976          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14977          * @param {String} value The string to truncate
14978          * @param {Number} length The maximum length to allow before truncating
14979          * @return {String} The converted text
14980          */
14981         ellipsis : function(value, len){
14982             if(value && value.length > len){
14983                 return value.substr(0, len-3)+"...";
14984             }
14985             return value;
14986         },
14987
14988         /**
14989          * Checks a reference and converts it to empty string if it is undefined
14990          * @param {Mixed} value Reference to check
14991          * @return {Mixed} Empty string if converted, otherwise the original value
14992          */
14993         undef : function(value){
14994             return typeof value != "undefined" ? value : "";
14995         },
14996
14997         /**
14998          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14999          * @param {String} value The string to encode
15000          * @return {String} The encoded text
15001          */
15002         htmlEncode : function(value){
15003             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
15004         },
15005
15006         /**
15007          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
15008          * @param {String} value The string to decode
15009          * @return {String} The decoded text
15010          */
15011         htmlDecode : function(value){
15012             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
15013         },
15014
15015         /**
15016          * Trims any whitespace from either side of a string
15017          * @param {String} value The text to trim
15018          * @return {String} The trimmed text
15019          */
15020         trim : function(value){
15021             return String(value).replace(trimRe, "");
15022         },
15023
15024         /**
15025          * Returns a substring from within an original string
15026          * @param {String} value The original text
15027          * @param {Number} start The start index of the substring
15028          * @param {Number} length The length of the substring
15029          * @return {String} The substring
15030          */
15031         substr : function(value, start, length){
15032             return String(value).substr(start, length);
15033         },
15034
15035         /**
15036          * Converts a string to all lower case letters
15037          * @param {String} value The text to convert
15038          * @return {String} The converted text
15039          */
15040         lowercase : function(value){
15041             return String(value).toLowerCase();
15042         },
15043
15044         /**
15045          * Converts a string to all upper case letters
15046          * @param {String} value The text to convert
15047          * @return {String} The converted text
15048          */
15049         uppercase : function(value){
15050             return String(value).toUpperCase();
15051         },
15052
15053         /**
15054          * Converts the first character only of a string to upper case
15055          * @param {String} value The text to convert
15056          * @return {String} The converted text
15057          */
15058         capitalize : function(value){
15059             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
15060         },
15061
15062         // private
15063         call : function(value, fn){
15064             if(arguments.length > 2){
15065                 var args = Array.prototype.slice.call(arguments, 2);
15066                 args.unshift(value);
15067                  
15068                 return /** eval:var:value */  eval(fn).apply(window, args);
15069             }else{
15070                 /** eval:var:value */
15071                 return /** eval:var:value */ eval(fn).call(window, value);
15072             }
15073         },
15074
15075        
15076         /**
15077          * safer version of Math.toFixed..??/
15078          * @param {Number/String} value The numeric value to format
15079          * @param {Number/String} value Decimal places 
15080          * @return {String} The formatted currency string
15081          */
15082         toFixed : function(v, n)
15083         {
15084             // why not use to fixed - precision is buggered???
15085             if (!n) {
15086                 return Math.round(v-0);
15087             }
15088             var fact = Math.pow(10,n+1);
15089             v = (Math.round((v-0)*fact))/fact;
15090             var z = (''+fact).substring(2);
15091             if (v == Math.floor(v)) {
15092                 return Math.floor(v) + '.' + z;
15093             }
15094             
15095             // now just padd decimals..
15096             var ps = String(v).split('.');
15097             var fd = (ps[1] + z);
15098             var r = fd.substring(0,n); 
15099             var rm = fd.substring(n); 
15100             if (rm < 5) {
15101                 return ps[0] + '.' + r;
15102             }
15103             r*=1; // turn it into a number;
15104             r++;
15105             if (String(r).length != n) {
15106                 ps[0]*=1;
15107                 ps[0]++;
15108                 r = String(r).substring(1); // chop the end off.
15109             }
15110             
15111             return ps[0] + '.' + r;
15112              
15113         },
15114         
15115         /**
15116          * Format a number as US currency
15117          * @param {Number/String} value The numeric value to format
15118          * @return {String} The formatted currency string
15119          */
15120         usMoney : function(v){
15121             return '$' + Roo.util.Format.number(v);
15122         },
15123         
15124         /**
15125          * Format a number
15126          * eventually this should probably emulate php's number_format
15127          * @param {Number/String} value The numeric value to format
15128          * @param {Number} decimals number of decimal places
15129          * @param {String} delimiter for thousands (default comma)
15130          * @return {String} The formatted currency string
15131          */
15132         number : function(v, decimals, thousandsDelimiter)
15133         {
15134             // multiply and round.
15135             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
15136             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
15137             
15138             var mul = Math.pow(10, decimals);
15139             var zero = String(mul).substring(1);
15140             v = (Math.round((v-0)*mul))/mul;
15141             
15142             // if it's '0' number.. then
15143             
15144             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
15145             v = String(v);
15146             var ps = v.split('.');
15147             var whole = ps[0];
15148             
15149             var r = /(\d+)(\d{3})/;
15150             // add comma's
15151             
15152             if(thousandsDelimiter.length != 0) {
15153                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
15154             } 
15155             
15156             var sub = ps[1] ?
15157                     // has decimals..
15158                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
15159                     // does not have decimals
15160                     (decimals ? ('.' + zero) : '');
15161             
15162             
15163             return whole + sub ;
15164         },
15165         
15166         /**
15167          * Parse a value into a formatted date using the specified format pattern.
15168          * @param {Mixed} value The value to format
15169          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
15170          * @return {String} The formatted date string
15171          */
15172         date : function(v, format){
15173             if(!v){
15174                 return "";
15175             }
15176             if(!(v instanceof Date)){
15177                 v = new Date(Date.parse(v));
15178             }
15179             return v.dateFormat(format || Roo.util.Format.defaults.date);
15180         },
15181
15182         /**
15183          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
15184          * @param {String} format Any valid date format string
15185          * @return {Function} The date formatting function
15186          */
15187         dateRenderer : function(format){
15188             return function(v){
15189                 return Roo.util.Format.date(v, format);  
15190             };
15191         },
15192
15193         // private
15194         stripTagsRE : /<\/?[^>]+>/gi,
15195         
15196         /**
15197          * Strips all HTML tags
15198          * @param {Mixed} value The text from which to strip tags
15199          * @return {String} The stripped text
15200          */
15201         stripTags : function(v){
15202             return !v ? v : String(v).replace(this.stripTagsRE, "");
15203         },
15204         
15205         /**
15206          * Size in Mb,Gb etc.
15207          * @param {Number} value The number to be formated
15208          * @param {number} decimals how many decimal places
15209          * @return {String} the formated string
15210          */
15211         size : function(value, decimals)
15212         {
15213             var sizes = ['b', 'k', 'M', 'G', 'T'];
15214             if (value == 0) {
15215                 return 0;
15216             }
15217             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
15218             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
15219         }
15220         
15221         
15222         
15223     };
15224 }();
15225 Roo.util.Format.defaults = {
15226     date : 'd/M/Y'
15227 };/*
15228  * Based on:
15229  * Ext JS Library 1.1.1
15230  * Copyright(c) 2006-2007, Ext JS, LLC.
15231  *
15232  * Originally Released Under LGPL - original licence link has changed is not relivant.
15233  *
15234  * Fork - LGPL
15235  * <script type="text/javascript">
15236  */
15237
15238
15239  
15240
15241 /**
15242  * @class Roo.MasterTemplate
15243  * @extends Roo.Template
15244  * Provides a template that can have child templates. The syntax is:
15245 <pre><code>
15246 var t = new Roo.MasterTemplate(
15247         '&lt;select name="{name}"&gt;',
15248                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
15249         '&lt;/select&gt;'
15250 );
15251 t.add('options', {value: 'foo', text: 'bar'});
15252 // or you can add multiple child elements in one shot
15253 t.addAll('options', [
15254     {value: 'foo', text: 'bar'},
15255     {value: 'foo2', text: 'bar2'},
15256     {value: 'foo3', text: 'bar3'}
15257 ]);
15258 // then append, applying the master template values
15259 t.append('my-form', {name: 'my-select'});
15260 </code></pre>
15261 * A name attribute for the child template is not required if you have only one child
15262 * template or you want to refer to them by index.
15263  */
15264 Roo.MasterTemplate = function(){
15265     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
15266     this.originalHtml = this.html;
15267     var st = {};
15268     var m, re = this.subTemplateRe;
15269     re.lastIndex = 0;
15270     var subIndex = 0;
15271     while(m = re.exec(this.html)){
15272         var name = m[1], content = m[2];
15273         st[subIndex] = {
15274             name: name,
15275             index: subIndex,
15276             buffer: [],
15277             tpl : new Roo.Template(content)
15278         };
15279         if(name){
15280             st[name] = st[subIndex];
15281         }
15282         st[subIndex].tpl.compile();
15283         st[subIndex].tpl.call = this.call.createDelegate(this);
15284         subIndex++;
15285     }
15286     this.subCount = subIndex;
15287     this.subs = st;
15288 };
15289 Roo.extend(Roo.MasterTemplate, Roo.Template, {
15290     /**
15291     * The regular expression used to match sub templates
15292     * @type RegExp
15293     * @property
15294     */
15295     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
15296
15297     /**
15298      * Applies the passed values to a child template.
15299      * @param {String/Number} name (optional) The name or index of the child template
15300      * @param {Array/Object} values The values to be applied to the template
15301      * @return {MasterTemplate} this
15302      */
15303      add : function(name, values){
15304         if(arguments.length == 1){
15305             values = arguments[0];
15306             name = 0;
15307         }
15308         var s = this.subs[name];
15309         s.buffer[s.buffer.length] = s.tpl.apply(values);
15310         return this;
15311     },
15312
15313     /**
15314      * Applies all the passed values to a child template.
15315      * @param {String/Number} name (optional) The name or index of the child template
15316      * @param {Array} values The values to be applied to the template, this should be an array of objects.
15317      * @param {Boolean} reset (optional) True to reset the template first
15318      * @return {MasterTemplate} this
15319      */
15320     fill : function(name, values, reset){
15321         var a = arguments;
15322         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15323             values = a[0];
15324             name = 0;
15325             reset = a[1];
15326         }
15327         if(reset){
15328             this.reset();
15329         }
15330         for(var i = 0, len = values.length; i < len; i++){
15331             this.add(name, values[i]);
15332         }
15333         return this;
15334     },
15335
15336     /**
15337      * Resets the template for reuse
15338      * @return {MasterTemplate} this
15339      */
15340      reset : function(){
15341         var s = this.subs;
15342         for(var i = 0; i < this.subCount; i++){
15343             s[i].buffer = [];
15344         }
15345         return this;
15346     },
15347
15348     applyTemplate : function(values){
15349         var s = this.subs;
15350         var replaceIndex = -1;
15351         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15352             return s[++replaceIndex].buffer.join("");
15353         });
15354         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15355     },
15356
15357     apply : function(){
15358         return this.applyTemplate.apply(this, arguments);
15359     },
15360
15361     compile : function(){return this;}
15362 });
15363
15364 /**
15365  * Alias for fill().
15366  * @method
15367  */
15368 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15369  /**
15370  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15371  * var tpl = Roo.MasterTemplate.from('element-id');
15372  * @param {String/HTMLElement} el
15373  * @param {Object} config
15374  * @static
15375  */
15376 Roo.MasterTemplate.from = function(el, config){
15377     el = Roo.getDom(el);
15378     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15379 };/*
15380  * Based on:
15381  * Ext JS Library 1.1.1
15382  * Copyright(c) 2006-2007, Ext JS, LLC.
15383  *
15384  * Originally Released Under LGPL - original licence link has changed is not relivant.
15385  *
15386  * Fork - LGPL
15387  * <script type="text/javascript">
15388  */
15389
15390  
15391 /**
15392  * @class Roo.util.CSS
15393  * Utility class for manipulating CSS rules
15394  * @static
15395
15396  */
15397 Roo.util.CSS = function(){
15398         var rules = null;
15399         var doc = document;
15400
15401     var camelRe = /(-[a-z])/gi;
15402     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15403
15404    return {
15405    /**
15406     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15407     * tag and appended to the HEAD of the document.
15408     * @param {String|Object} cssText The text containing the css rules
15409     * @param {String} id An id to add to the stylesheet for later removal
15410     * @return {StyleSheet}
15411     */
15412     createStyleSheet : function(cssText, id){
15413         var ss;
15414         var head = doc.getElementsByTagName("head")[0];
15415         var nrules = doc.createElement("style");
15416         nrules.setAttribute("type", "text/css");
15417         if(id){
15418             nrules.setAttribute("id", id);
15419         }
15420         if (typeof(cssText) != 'string') {
15421             // support object maps..
15422             // not sure if this a good idea.. 
15423             // perhaps it should be merged with the general css handling
15424             // and handle js style props.
15425             var cssTextNew = [];
15426             for(var n in cssText) {
15427                 var citems = [];
15428                 for(var k in cssText[n]) {
15429                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15430                 }
15431                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15432                 
15433             }
15434             cssText = cssTextNew.join("\n");
15435             
15436         }
15437        
15438        
15439        if(Roo.isIE){
15440            head.appendChild(nrules);
15441            ss = nrules.styleSheet;
15442            ss.cssText = cssText;
15443        }else{
15444            try{
15445                 nrules.appendChild(doc.createTextNode(cssText));
15446            }catch(e){
15447                nrules.cssText = cssText; 
15448            }
15449            head.appendChild(nrules);
15450            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15451        }
15452        this.cacheStyleSheet(ss);
15453        return ss;
15454    },
15455
15456    /**
15457     * Removes a style or link tag by id
15458     * @param {String} id The id of the tag
15459     */
15460    removeStyleSheet : function(id){
15461        var existing = doc.getElementById(id);
15462        if(existing){
15463            existing.parentNode.removeChild(existing);
15464        }
15465    },
15466
15467    /**
15468     * Dynamically swaps an existing stylesheet reference for a new one
15469     * @param {String} id The id of an existing link tag to remove
15470     * @param {String} url The href of the new stylesheet to include
15471     */
15472    swapStyleSheet : function(id, url){
15473        this.removeStyleSheet(id);
15474        var ss = doc.createElement("link");
15475        ss.setAttribute("rel", "stylesheet");
15476        ss.setAttribute("type", "text/css");
15477        ss.setAttribute("id", id);
15478        ss.setAttribute("href", url);
15479        doc.getElementsByTagName("head")[0].appendChild(ss);
15480    },
15481    
15482    /**
15483     * Refresh the rule cache if you have dynamically added stylesheets
15484     * @return {Object} An object (hash) of rules indexed by selector
15485     */
15486    refreshCache : function(){
15487        return this.getRules(true);
15488    },
15489
15490    // private
15491    cacheStyleSheet : function(stylesheet){
15492        if(!rules){
15493            rules = {};
15494        }
15495        try{// try catch for cross domain access issue
15496            var ssRules = stylesheet.cssRules || stylesheet.rules;
15497            for(var j = ssRules.length-1; j >= 0; --j){
15498                rules[ssRules[j].selectorText] = ssRules[j];
15499            }
15500        }catch(e){}
15501    },
15502    
15503    /**
15504     * Gets all css rules for the document
15505     * @param {Boolean} refreshCache true to refresh the internal cache
15506     * @return {Object} An object (hash) of rules indexed by selector
15507     */
15508    getRules : function(refreshCache){
15509                 if(rules == null || refreshCache){
15510                         rules = {};
15511                         var ds = doc.styleSheets;
15512                         for(var i =0, len = ds.length; i < len; i++){
15513                             try{
15514                         this.cacheStyleSheet(ds[i]);
15515                     }catch(e){} 
15516                 }
15517                 }
15518                 return rules;
15519         },
15520         
15521         /**
15522     * Gets an an individual CSS rule by selector(s)
15523     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15524     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15525     * @return {CSSRule} The CSS rule or null if one is not found
15526     */
15527    getRule : function(selector, refreshCache){
15528                 var rs = this.getRules(refreshCache);
15529                 if(!(selector instanceof Array)){
15530                     return rs[selector];
15531                 }
15532                 for(var i = 0; i < selector.length; i++){
15533                         if(rs[selector[i]]){
15534                                 return rs[selector[i]];
15535                         }
15536                 }
15537                 return null;
15538         },
15539         
15540         
15541         /**
15542     * Updates a rule property
15543     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15544     * @param {String} property The css property
15545     * @param {String} value The new value for the property
15546     * @return {Boolean} true If a rule was found and updated
15547     */
15548    updateRule : function(selector, property, value){
15549                 if(!(selector instanceof Array)){
15550                         var rule = this.getRule(selector);
15551                         if(rule){
15552                                 rule.style[property.replace(camelRe, camelFn)] = value;
15553                                 return true;
15554                         }
15555                 }else{
15556                         for(var i = 0; i < selector.length; i++){
15557                                 if(this.updateRule(selector[i], property, value)){
15558                                         return true;
15559                                 }
15560                         }
15561                 }
15562                 return false;
15563         }
15564    };   
15565 }();/*
15566  * Based on:
15567  * Ext JS Library 1.1.1
15568  * Copyright(c) 2006-2007, Ext JS, LLC.
15569  *
15570  * Originally Released Under LGPL - original licence link has changed is not relivant.
15571  *
15572  * Fork - LGPL
15573  * <script type="text/javascript">
15574  */
15575
15576  
15577
15578 /**
15579  * @class Roo.util.ClickRepeater
15580  * @extends Roo.util.Observable
15581  * 
15582  * A wrapper class which can be applied to any element. Fires a "click" event while the
15583  * mouse is pressed. The interval between firings may be specified in the config but
15584  * defaults to 10 milliseconds.
15585  * 
15586  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15587  * 
15588  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15589  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15590  * Similar to an autorepeat key delay.
15591  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15592  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15593  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15594  *           "interval" and "delay" are ignored. "immediate" is honored.
15595  * @cfg {Boolean} preventDefault True to prevent the default click event
15596  * @cfg {Boolean} stopDefault True to stop the default click event
15597  * 
15598  * @history
15599  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15600  *     2007-02-02 jvs Renamed to ClickRepeater
15601  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15602  *
15603  *  @constructor
15604  * @param {String/HTMLElement/Element} el The element to listen on
15605  * @param {Object} config
15606  **/
15607 Roo.util.ClickRepeater = function(el, config)
15608 {
15609     this.el = Roo.get(el);
15610     this.el.unselectable();
15611
15612     Roo.apply(this, config);
15613
15614     this.addEvents({
15615     /**
15616      * @event mousedown
15617      * Fires when the mouse button is depressed.
15618      * @param {Roo.util.ClickRepeater} this
15619      */
15620         "mousedown" : true,
15621     /**
15622      * @event click
15623      * Fires on a specified interval during the time the element is pressed.
15624      * @param {Roo.util.ClickRepeater} this
15625      */
15626         "click" : true,
15627     /**
15628      * @event mouseup
15629      * Fires when the mouse key is released.
15630      * @param {Roo.util.ClickRepeater} this
15631      */
15632         "mouseup" : true
15633     });
15634
15635     this.el.on("mousedown", this.handleMouseDown, this);
15636     if(this.preventDefault || this.stopDefault){
15637         this.el.on("click", function(e){
15638             if(this.preventDefault){
15639                 e.preventDefault();
15640             }
15641             if(this.stopDefault){
15642                 e.stopEvent();
15643             }
15644         }, this);
15645     }
15646
15647     // allow inline handler
15648     if(this.handler){
15649         this.on("click", this.handler,  this.scope || this);
15650     }
15651
15652     Roo.util.ClickRepeater.superclass.constructor.call(this);
15653 };
15654
15655 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15656     interval : 20,
15657     delay: 250,
15658     preventDefault : true,
15659     stopDefault : false,
15660     timer : 0,
15661
15662     // private
15663     handleMouseDown : function(){
15664         clearTimeout(this.timer);
15665         this.el.blur();
15666         if(this.pressClass){
15667             this.el.addClass(this.pressClass);
15668         }
15669         this.mousedownTime = new Date();
15670
15671         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15672         this.el.on("mouseout", this.handleMouseOut, this);
15673
15674         this.fireEvent("mousedown", this);
15675         this.fireEvent("click", this);
15676         
15677         this.timer = this.click.defer(this.delay || this.interval, this);
15678     },
15679
15680     // private
15681     click : function(){
15682         this.fireEvent("click", this);
15683         this.timer = this.click.defer(this.getInterval(), this);
15684     },
15685
15686     // private
15687     getInterval: function(){
15688         if(!this.accelerate){
15689             return this.interval;
15690         }
15691         var pressTime = this.mousedownTime.getElapsed();
15692         if(pressTime < 500){
15693             return 400;
15694         }else if(pressTime < 1700){
15695             return 320;
15696         }else if(pressTime < 2600){
15697             return 250;
15698         }else if(pressTime < 3500){
15699             return 180;
15700         }else if(pressTime < 4400){
15701             return 140;
15702         }else if(pressTime < 5300){
15703             return 80;
15704         }else if(pressTime < 6200){
15705             return 50;
15706         }else{
15707             return 10;
15708         }
15709     },
15710
15711     // private
15712     handleMouseOut : function(){
15713         clearTimeout(this.timer);
15714         if(this.pressClass){
15715             this.el.removeClass(this.pressClass);
15716         }
15717         this.el.on("mouseover", this.handleMouseReturn, this);
15718     },
15719
15720     // private
15721     handleMouseReturn : function(){
15722         this.el.un("mouseover", this.handleMouseReturn);
15723         if(this.pressClass){
15724             this.el.addClass(this.pressClass);
15725         }
15726         this.click();
15727     },
15728
15729     // private
15730     handleMouseUp : function(){
15731         clearTimeout(this.timer);
15732         this.el.un("mouseover", this.handleMouseReturn);
15733         this.el.un("mouseout", this.handleMouseOut);
15734         Roo.get(document).un("mouseup", this.handleMouseUp);
15735         this.el.removeClass(this.pressClass);
15736         this.fireEvent("mouseup", this);
15737     }
15738 });/**
15739  * @class Roo.util.Clipboard
15740  * @static
15741  * 
15742  * Clipboard UTILS
15743  * 
15744  **/
15745 Roo.util.Clipboard = {
15746     /**
15747      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15748      * @param {String} text to copy to clipboard
15749      */
15750     write : function(text) {
15751         // navigator clipboard api needs a secure context (https)
15752         if (navigator.clipboard && window.isSecureContext) {
15753             // navigator clipboard api method'
15754             navigator.clipboard.writeText(text);
15755             return ;
15756         } 
15757         // text area method
15758         var ta = document.createElement("textarea");
15759         ta.value = text;
15760         // make the textarea out of viewport
15761         ta.style.position = "fixed";
15762         ta.style.left = "-999999px";
15763         ta.style.top = "-999999px";
15764         document.body.appendChild(ta);
15765         ta.focus();
15766         ta.select();
15767         document.execCommand('copy');
15768         (function() {
15769             ta.remove();
15770         }).defer(100);
15771         
15772     }
15773         
15774 }
15775     /*
15776  * Based on:
15777  * Ext JS Library 1.1.1
15778  * Copyright(c) 2006-2007, Ext JS, LLC.
15779  *
15780  * Originally Released Under LGPL - original licence link has changed is not relivant.
15781  *
15782  * Fork - LGPL
15783  * <script type="text/javascript">
15784  */
15785
15786  
15787 /**
15788  * @class Roo.KeyNav
15789  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15790  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15791  * way to implement custom navigation schemes for any UI component.</p>
15792  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15793  * pageUp, pageDown, del, home, end.  Usage:</p>
15794  <pre><code>
15795 var nav = new Roo.KeyNav("my-element", {
15796     "left" : function(e){
15797         this.moveLeft(e.ctrlKey);
15798     },
15799     "right" : function(e){
15800         this.moveRight(e.ctrlKey);
15801     },
15802     "enter" : function(e){
15803         this.save();
15804     },
15805     scope : this
15806 });
15807 </code></pre>
15808  * @constructor
15809  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15810  * @param {Object} config The config
15811  */
15812 Roo.KeyNav = function(el, config){
15813     this.el = Roo.get(el);
15814     Roo.apply(this, config);
15815     if(!this.disabled){
15816         this.disabled = true;
15817         this.enable();
15818     }
15819 };
15820
15821 Roo.KeyNav.prototype = {
15822     /**
15823      * @cfg {Boolean} disabled
15824      * True to disable this KeyNav instance (defaults to false)
15825      */
15826     disabled : false,
15827     /**
15828      * @cfg {String} defaultEventAction
15829      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15830      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15831      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15832      */
15833     defaultEventAction: "stopEvent",
15834     /**
15835      * @cfg {Boolean} forceKeyDown
15836      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15837      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15838      * handle keydown instead of keypress.
15839      */
15840     forceKeyDown : false,
15841
15842     // private
15843     prepareEvent : function(e){
15844         var k = e.getKey();
15845         var h = this.keyToHandler[k];
15846         //if(h && this[h]){
15847         //    e.stopPropagation();
15848         //}
15849         if(Roo.isSafari && h && k >= 37 && k <= 40){
15850             e.stopEvent();
15851         }
15852     },
15853
15854     // private
15855     relay : function(e){
15856         var k = e.getKey();
15857         var h = this.keyToHandler[k];
15858         if(h && this[h]){
15859             if(this.doRelay(e, this[h], h) !== true){
15860                 e[this.defaultEventAction]();
15861             }
15862         }
15863     },
15864
15865     // private
15866     doRelay : function(e, h, hname){
15867         return h.call(this.scope || this, e);
15868     },
15869
15870     // possible handlers
15871     enter : false,
15872     left : false,
15873     right : false,
15874     up : false,
15875     down : false,
15876     tab : false,
15877     esc : false,
15878     pageUp : false,
15879     pageDown : false,
15880     del : false,
15881     home : false,
15882     end : false,
15883
15884     // quick lookup hash
15885     keyToHandler : {
15886         37 : "left",
15887         39 : "right",
15888         38 : "up",
15889         40 : "down",
15890         33 : "pageUp",
15891         34 : "pageDown",
15892         46 : "del",
15893         36 : "home",
15894         35 : "end",
15895         13 : "enter",
15896         27 : "esc",
15897         9  : "tab"
15898     },
15899
15900         /**
15901          * Enable this KeyNav
15902          */
15903         enable: function(){
15904                 if(this.disabled){
15905             // ie won't do special keys on keypress, no one else will repeat keys with keydown
15906             // the EventObject will normalize Safari automatically
15907             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15908                 this.el.on("keydown", this.relay,  this);
15909             }else{
15910                 this.el.on("keydown", this.prepareEvent,  this);
15911                 this.el.on("keypress", this.relay,  this);
15912             }
15913                     this.disabled = false;
15914                 }
15915         },
15916
15917         /**
15918          * Disable this KeyNav
15919          */
15920         disable: function(){
15921                 if(!this.disabled){
15922                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15923                 this.el.un("keydown", this.relay);
15924             }else{
15925                 this.el.un("keydown", this.prepareEvent);
15926                 this.el.un("keypress", this.relay);
15927             }
15928                     this.disabled = true;
15929                 }
15930         }
15931 };/*
15932  * Based on:
15933  * Ext JS Library 1.1.1
15934  * Copyright(c) 2006-2007, Ext JS, LLC.
15935  *
15936  * Originally Released Under LGPL - original licence link has changed is not relivant.
15937  *
15938  * Fork - LGPL
15939  * <script type="text/javascript">
15940  */
15941
15942  
15943 /**
15944  * @class Roo.KeyMap
15945  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15946  * The constructor accepts the same config object as defined by {@link #addBinding}.
15947  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15948  * combination it will call the function with this signature (if the match is a multi-key
15949  * combination the callback will still be called only once): (String key, Roo.EventObject e)
15950  * A KeyMap can also handle a string representation of keys.<br />
15951  * Usage:
15952  <pre><code>
15953 // map one key by key code
15954 var map = new Roo.KeyMap("my-element", {
15955     key: 13, // or Roo.EventObject.ENTER
15956     fn: myHandler,
15957     scope: myObject
15958 });
15959
15960 // map multiple keys to one action by string
15961 var map = new Roo.KeyMap("my-element", {
15962     key: "a\r\n\t",
15963     fn: myHandler,
15964     scope: myObject
15965 });
15966
15967 // map multiple keys to multiple actions by strings and array of codes
15968 var map = new Roo.KeyMap("my-element", [
15969     {
15970         key: [10,13],
15971         fn: function(){ alert("Return was pressed"); }
15972     }, {
15973         key: "abc",
15974         fn: function(){ alert('a, b or c was pressed'); }
15975     }, {
15976         key: "\t",
15977         ctrl:true,
15978         shift:true,
15979         fn: function(){ alert('Control + shift + tab was pressed.'); }
15980     }
15981 ]);
15982 </code></pre>
15983  * <b>Note: A KeyMap starts enabled</b>
15984  * @constructor
15985  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15986  * @param {Object} config The config (see {@link #addBinding})
15987  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15988  */
15989 Roo.KeyMap = function(el, config, eventName){
15990     this.el  = Roo.get(el);
15991     this.eventName = eventName || "keydown";
15992     this.bindings = [];
15993     if(config){
15994         this.addBinding(config);
15995     }
15996     this.enable();
15997 };
15998
15999 Roo.KeyMap.prototype = {
16000     /**
16001      * True to stop the event from bubbling and prevent the default browser action if the
16002      * key was handled by the KeyMap (defaults to false)
16003      * @type Boolean
16004      */
16005     stopEvent : false,
16006
16007     /**
16008      * Add a new binding to this KeyMap. The following config object properties are supported:
16009      * <pre>
16010 Property    Type             Description
16011 ----------  ---------------  ----------------------------------------------------------------------
16012 key         String/Array     A single keycode or an array of keycodes to handle
16013 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
16014 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
16015 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
16016 fn          Function         The function to call when KeyMap finds the expected key combination
16017 scope       Object           The scope of the callback function
16018 </pre>
16019      *
16020      * Usage:
16021      * <pre><code>
16022 // Create a KeyMap
16023 var map = new Roo.KeyMap(document, {
16024     key: Roo.EventObject.ENTER,
16025     fn: handleKey,
16026     scope: this
16027 });
16028
16029 //Add a new binding to the existing KeyMap later
16030 map.addBinding({
16031     key: 'abc',
16032     shift: true,
16033     fn: handleKey,
16034     scope: this
16035 });
16036 </code></pre>
16037      * @param {Object/Array} config A single KeyMap config or an array of configs
16038      */
16039         addBinding : function(config){
16040         if(config instanceof Array){
16041             for(var i = 0, len = config.length; i < len; i++){
16042                 this.addBinding(config[i]);
16043             }
16044             return;
16045         }
16046         var keyCode = config.key,
16047             shift = config.shift, 
16048             ctrl = config.ctrl, 
16049             alt = config.alt,
16050             fn = config.fn,
16051             scope = config.scope;
16052         if(typeof keyCode == "string"){
16053             var ks = [];
16054             var keyString = keyCode.toUpperCase();
16055             for(var j = 0, len = keyString.length; j < len; j++){
16056                 ks.push(keyString.charCodeAt(j));
16057             }
16058             keyCode = ks;
16059         }
16060         var keyArray = keyCode instanceof Array;
16061         var handler = function(e){
16062             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
16063                 var k = e.getKey();
16064                 if(keyArray){
16065                     for(var i = 0, len = keyCode.length; i < len; i++){
16066                         if(keyCode[i] == k){
16067                           if(this.stopEvent){
16068                               e.stopEvent();
16069                           }
16070                           fn.call(scope || window, k, e);
16071                           return;
16072                         }
16073                     }
16074                 }else{
16075                     if(k == keyCode){
16076                         if(this.stopEvent){
16077                            e.stopEvent();
16078                         }
16079                         fn.call(scope || window, k, e);
16080                     }
16081                 }
16082             }
16083         };
16084         this.bindings.push(handler);  
16085         },
16086
16087     /**
16088      * Shorthand for adding a single key listener
16089      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
16090      * following options:
16091      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
16092      * @param {Function} fn The function to call
16093      * @param {Object} scope (optional) The scope of the function
16094      */
16095     on : function(key, fn, scope){
16096         var keyCode, shift, ctrl, alt;
16097         if(typeof key == "object" && !(key instanceof Array)){
16098             keyCode = key.key;
16099             shift = key.shift;
16100             ctrl = key.ctrl;
16101             alt = key.alt;
16102         }else{
16103             keyCode = key;
16104         }
16105         this.addBinding({
16106             key: keyCode,
16107             shift: shift,
16108             ctrl: ctrl,
16109             alt: alt,
16110             fn: fn,
16111             scope: scope
16112         })
16113     },
16114
16115     // private
16116     handleKeyDown : function(e){
16117             if(this.enabled){ //just in case
16118             var b = this.bindings;
16119             for(var i = 0, len = b.length; i < len; i++){
16120                 b[i].call(this, e);
16121             }
16122             }
16123         },
16124         
16125         /**
16126          * Returns true if this KeyMap is enabled
16127          * @return {Boolean} 
16128          */
16129         isEnabled : function(){
16130             return this.enabled;  
16131         },
16132         
16133         /**
16134          * Enables this KeyMap
16135          */
16136         enable: function(){
16137                 if(!this.enabled){
16138                     this.el.on(this.eventName, this.handleKeyDown, this);
16139                     this.enabled = true;
16140                 }
16141         },
16142
16143         /**
16144          * Disable this KeyMap
16145          */
16146         disable: function(){
16147                 if(this.enabled){
16148                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
16149                     this.enabled = false;
16150                 }
16151         }
16152 };/*
16153  * Based on:
16154  * Ext JS Library 1.1.1
16155  * Copyright(c) 2006-2007, Ext JS, LLC.
16156  *
16157  * Originally Released Under LGPL - original licence link has changed is not relivant.
16158  *
16159  * Fork - LGPL
16160  * <script type="text/javascript">
16161  */
16162
16163  
16164 /**
16165  * @class Roo.util.TextMetrics
16166  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
16167  * wide, in pixels, a given block of text will be.
16168  * @static
16169  */
16170 Roo.util.TextMetrics = function(){
16171     var shared;
16172     return {
16173         /**
16174          * Measures the size of the specified text
16175          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
16176          * that can affect the size of the rendered text
16177          * @param {String} text The text to measure
16178          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16179          * in order to accurately measure the text height
16180          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16181          */
16182         measure : function(el, text, fixedWidth){
16183             if(!shared){
16184                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
16185             }
16186             shared.bind(el);
16187             shared.setFixedWidth(fixedWidth || 'auto');
16188             return shared.getSize(text);
16189         },
16190
16191         /**
16192          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
16193          * the overhead of multiple calls to initialize the style properties on each measurement.
16194          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
16195          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16196          * in order to accurately measure the text height
16197          * @return {Roo.util.TextMetrics.Instance} instance The new instance
16198          */
16199         createInstance : function(el, fixedWidth){
16200             return Roo.util.TextMetrics.Instance(el, fixedWidth);
16201         }
16202     };
16203 }();
16204
16205 /**
16206  * @class Roo.util.TextMetrics.Instance
16207  * Instance of  TextMetrics Calcuation
16208  * @constructor
16209  * Create a new TextMetrics Instance
16210  * @param {Object} bindto
16211  * @param {Boolean} fixedWidth
16212  */
16213
16214 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
16215 {
16216     var ml = new Roo.Element(document.createElement('div'));
16217     document.body.appendChild(ml.dom);
16218     ml.position('absolute');
16219     ml.setLeftTop(-1000, -1000);
16220     ml.hide();
16221
16222     if(fixedWidth){
16223         ml.setWidth(fixedWidth);
16224     }
16225      
16226     var instance = {
16227         /**
16228          * Returns the size of the specified text based on the internal element's style and width properties
16229          * @param {String} text The text to measure
16230          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16231          */
16232         getSize : function(text){
16233             ml.update(text);
16234             var s = ml.getSize();
16235             ml.update('');
16236             return s;
16237         },
16238
16239         /**
16240          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
16241          * that can affect the size of the rendered text
16242          * @param {String/HTMLElement} el The element, dom node or id
16243          */
16244         bind : function(el){
16245             ml.setStyle(
16246                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
16247             );
16248         },
16249
16250         /**
16251          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
16252          * to set a fixed width in order to accurately measure the text height.
16253          * @param {Number} width The width to set on the element
16254          */
16255         setFixedWidth : function(width){
16256             ml.setWidth(width);
16257         },
16258
16259         /**
16260          * Returns the measured width of the specified text
16261          * @param {String} text The text to measure
16262          * @return {Number} width The width in pixels
16263          */
16264         getWidth : function(text){
16265             ml.dom.style.width = 'auto';
16266             return this.getSize(text).width;
16267         },
16268
16269         /**
16270          * Returns the measured height of the specified text.  For multiline text, be sure to call
16271          * {@link #setFixedWidth} if necessary.
16272          * @param {String} text The text to measure
16273          * @return {Number} height The height in pixels
16274          */
16275         getHeight : function(text){
16276             return this.getSize(text).height;
16277         }
16278     };
16279
16280     instance.bind(bindTo);
16281
16282     return instance;
16283 };
16284
16285 // backwards compat
16286 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
16287  * Based on:
16288  * Ext JS Library 1.1.1
16289  * Copyright(c) 2006-2007, Ext JS, LLC.
16290  *
16291  * Originally Released Under LGPL - original licence link has changed is not relivant.
16292  *
16293  * Fork - LGPL
16294  * <script type="text/javascript">
16295  */
16296
16297 /**
16298  * @class Roo.state.Provider
16299  * Abstract base class for state provider implementations. This class provides methods
16300  * for encoding and decoding <b>typed</b> variables including dates and defines the 
16301  * Provider interface.
16302  */
16303 Roo.state.Provider = function(){
16304     /**
16305      * @event statechange
16306      * Fires when a state change occurs.
16307      * @param {Provider} this This state provider
16308      * @param {String} key The state key which was changed
16309      * @param {String} value The encoded value for the state
16310      */
16311     this.addEvents({
16312         "statechange": true
16313     });
16314     this.state = {};
16315     Roo.state.Provider.superclass.constructor.call(this);
16316 };
16317 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16318     /**
16319      * Returns the current value for a key
16320      * @param {String} name The key name
16321      * @param {Mixed} defaultValue A default value to return if the key's value is not found
16322      * @return {Mixed} The state data
16323      */
16324     get : function(name, defaultValue){
16325         return typeof this.state[name] == "undefined" ?
16326             defaultValue : this.state[name];
16327     },
16328     
16329     /**
16330      * Clears a value from the state
16331      * @param {String} name The key name
16332      */
16333     clear : function(name){
16334         delete this.state[name];
16335         this.fireEvent("statechange", this, name, null);
16336     },
16337     
16338     /**
16339      * Sets the value for a key
16340      * @param {String} name The key name
16341      * @param {Mixed} value The value to set
16342      */
16343     set : function(name, value){
16344         this.state[name] = value;
16345         this.fireEvent("statechange", this, name, value);
16346     },
16347     
16348     /**
16349      * Decodes a string previously encoded with {@link #encodeValue}.
16350      * @param {String} value The value to decode
16351      * @return {Mixed} The decoded value
16352      */
16353     decodeValue : function(cookie){
16354         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16355         var matches = re.exec(unescape(cookie));
16356         if(!matches || !matches[1]) {
16357             return; // non state cookie
16358         }
16359         var type = matches[1];
16360         var v = matches[2];
16361         switch(type){
16362             case "n":
16363                 return parseFloat(v);
16364             case "d":
16365                 return new Date(Date.parse(v));
16366             case "b":
16367                 return (v == "1");
16368             case "a":
16369                 var all = [];
16370                 var values = v.split("^");
16371                 for(var i = 0, len = values.length; i < len; i++){
16372                     all.push(this.decodeValue(values[i]));
16373                 }
16374                 return all;
16375            case "o":
16376                 var all = {};
16377                 var values = v.split("^");
16378                 for(var i = 0, len = values.length; i < len; i++){
16379                     var kv = values[i].split("=");
16380                     all[kv[0]] = this.decodeValue(kv[1]);
16381                 }
16382                 return all;
16383            default:
16384                 return v;
16385         }
16386     },
16387     
16388     /**
16389      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16390      * @param {Mixed} value The value to encode
16391      * @return {String} The encoded value
16392      */
16393     encodeValue : function(v){
16394         var enc;
16395         if(typeof v == "number"){
16396             enc = "n:" + v;
16397         }else if(typeof v == "boolean"){
16398             enc = "b:" + (v ? "1" : "0");
16399         }else if(v instanceof Date){
16400             enc = "d:" + v.toGMTString();
16401         }else if(v instanceof Array){
16402             var flat = "";
16403             for(var i = 0, len = v.length; i < len; i++){
16404                 flat += this.encodeValue(v[i]);
16405                 if(i != len-1) {
16406                     flat += "^";
16407                 }
16408             }
16409             enc = "a:" + flat;
16410         }else if(typeof v == "object"){
16411             var flat = "";
16412             for(var key in v){
16413                 if(typeof v[key] != "function"){
16414                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16415                 }
16416             }
16417             enc = "o:" + flat.substring(0, flat.length-1);
16418         }else{
16419             enc = "s:" + v;
16420         }
16421         return escape(enc);        
16422     }
16423 });
16424
16425 /*
16426  * Based on:
16427  * Ext JS Library 1.1.1
16428  * Copyright(c) 2006-2007, Ext JS, LLC.
16429  *
16430  * Originally Released Under LGPL - original licence link has changed is not relivant.
16431  *
16432  * Fork - LGPL
16433  * <script type="text/javascript">
16434  */
16435 /**
16436  * @class Roo.state.Manager
16437  * This is the global state manager. By default all components that are "state aware" check this class
16438  * for state information if you don't pass them a custom state provider. In order for this class
16439  * to be useful, it must be initialized with a provider when your application initializes.
16440  <pre><code>
16441 // in your initialization function
16442 init : function(){
16443    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16444    ...
16445    // supposed you have a {@link Roo.BorderLayout}
16446    var layout = new Roo.BorderLayout(...);
16447    layout.restoreState();
16448    // or a {Roo.BasicDialog}
16449    var dialog = new Roo.BasicDialog(...);
16450    dialog.restoreState();
16451  </code></pre>
16452  * @static
16453  */
16454 Roo.state.Manager = function(){
16455     var provider = new Roo.state.Provider();
16456     
16457     return {
16458         /**
16459          * Configures the default state provider for your application
16460          * @param {Provider} stateProvider The state provider to set
16461          */
16462         setProvider : function(stateProvider){
16463             provider = stateProvider;
16464         },
16465         
16466         /**
16467          * Returns the current value for a key
16468          * @param {String} name The key name
16469          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16470          * @return {Mixed} The state data
16471          */
16472         get : function(key, defaultValue){
16473             return provider.get(key, defaultValue);
16474         },
16475         
16476         /**
16477          * Sets the value for a key
16478          * @param {String} name The key name
16479          * @param {Mixed} value The state data
16480          */
16481          set : function(key, value){
16482             provider.set(key, value);
16483         },
16484         
16485         /**
16486          * Clears a value from the state
16487          * @param {String} name The key name
16488          */
16489         clear : function(key){
16490             provider.clear(key);
16491         },
16492         
16493         /**
16494          * Gets the currently configured state provider
16495          * @return {Provider} The state provider
16496          */
16497         getProvider : function(){
16498             return provider;
16499         }
16500     };
16501 }();
16502 /*
16503  * Based on:
16504  * Ext JS Library 1.1.1
16505  * Copyright(c) 2006-2007, Ext JS, LLC.
16506  *
16507  * Originally Released Under LGPL - original licence link has changed is not relivant.
16508  *
16509  * Fork - LGPL
16510  * <script type="text/javascript">
16511  */
16512 /**
16513  * @class Roo.state.CookieProvider
16514  * @extends Roo.state.Provider
16515  * The default Provider implementation which saves state via cookies.
16516  * <br />Usage:
16517  <pre><code>
16518    var cp = new Roo.state.CookieProvider({
16519        path: "/cgi-bin/",
16520        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16521        domain: "roojs.com"
16522    })
16523    Roo.state.Manager.setProvider(cp);
16524  </code></pre>
16525  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16526  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16527  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16528  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16529  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16530  * domain the page is running on including the 'www' like 'www.roojs.com')
16531  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16532  * @constructor
16533  * Create a new CookieProvider
16534  * @param {Object} config The configuration object
16535  */
16536 Roo.state.CookieProvider = function(config){
16537     Roo.state.CookieProvider.superclass.constructor.call(this);
16538     this.path = "/";
16539     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16540     this.domain = null;
16541     this.secure = false;
16542     Roo.apply(this, config);
16543     this.state = this.readCookies();
16544 };
16545
16546 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16547     // private
16548     set : function(name, value){
16549         if(typeof value == "undefined" || value === null){
16550             this.clear(name);
16551             return;
16552         }
16553         this.setCookie(name, value);
16554         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16555     },
16556
16557     // private
16558     clear : function(name){
16559         this.clearCookie(name);
16560         Roo.state.CookieProvider.superclass.clear.call(this, name);
16561     },
16562
16563     // private
16564     readCookies : function(){
16565         var cookies = {};
16566         var c = document.cookie + ";";
16567         var re = /\s?(.*?)=(.*?);/g;
16568         var matches;
16569         while((matches = re.exec(c)) != null){
16570             var name = matches[1];
16571             var value = matches[2];
16572             if(name && name.substring(0,3) == "ys-"){
16573                 cookies[name.substr(3)] = this.decodeValue(value);
16574             }
16575         }
16576         return cookies;
16577     },
16578
16579     // private
16580     setCookie : function(name, value){
16581         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16582            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16583            ((this.path == null) ? "" : ("; path=" + this.path)) +
16584            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16585            ((this.secure == true) ? "; secure" : "");
16586     },
16587
16588     // private
16589     clearCookie : function(name){
16590         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16591            ((this.path == null) ? "" : ("; path=" + this.path)) +
16592            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16593            ((this.secure == true) ? "; secure" : "");
16594     }
16595 });/*
16596  * Based on:
16597  * Ext JS Library 1.1.1
16598  * Copyright(c) 2006-2007, Ext JS, LLC.
16599  *
16600  * Originally Released Under LGPL - original licence link has changed is not relivant.
16601  *
16602  * Fork - LGPL
16603  * <script type="text/javascript">
16604  */
16605  
16606
16607 /**
16608  * @class Roo.ComponentMgr
16609  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16610  * @static
16611  */
16612 Roo.ComponentMgr = function(){
16613     var all = new Roo.util.MixedCollection();
16614
16615     return {
16616         /**
16617          * Registers a component.
16618          * @param {Roo.Component} c The component
16619          */
16620         register : function(c){
16621             all.add(c);
16622         },
16623
16624         /**
16625          * Unregisters a component.
16626          * @param {Roo.Component} c The component
16627          */
16628         unregister : function(c){
16629             all.remove(c);
16630         },
16631
16632         /**
16633          * Returns a component by id
16634          * @param {String} id The component id
16635          */
16636         get : function(id){
16637             return all.get(id);
16638         },
16639
16640         /**
16641          * Registers a function that will be called when a specified component is added to ComponentMgr
16642          * @param {String} id The component id
16643          * @param {Funtction} fn The callback function
16644          * @param {Object} scope The scope of the callback
16645          */
16646         onAvailable : function(id, fn, scope){
16647             all.on("add", function(index, o){
16648                 if(o.id == id){
16649                     fn.call(scope || o, o);
16650                     all.un("add", fn, scope);
16651                 }
16652             });
16653         }
16654     };
16655 }();/*
16656  * Based on:
16657  * Ext JS Library 1.1.1
16658  * Copyright(c) 2006-2007, Ext JS, LLC.
16659  *
16660  * Originally Released Under LGPL - original licence link has changed is not relivant.
16661  *
16662  * Fork - LGPL
16663  * <script type="text/javascript">
16664  */
16665  
16666 /**
16667  * @class Roo.Component
16668  * @extends Roo.util.Observable
16669  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16670  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16671  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16672  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16673  * All visual components (widgets) that require rendering into a layout should subclass Component.
16674  * @constructor
16675  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16676  * 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
16677  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16678  */
16679 Roo.Component = function(config){
16680     config = config || {};
16681     if(config.tagName || config.dom || typeof config == "string"){ // element object
16682         config = {el: config, id: config.id || config};
16683     }
16684     this.initialConfig = config;
16685
16686     Roo.apply(this, config);
16687     this.addEvents({
16688         /**
16689          * @event disable
16690          * Fires after the component is disabled.
16691              * @param {Roo.Component} this
16692              */
16693         disable : true,
16694         /**
16695          * @event enable
16696          * Fires after the component is enabled.
16697              * @param {Roo.Component} this
16698              */
16699         enable : true,
16700         /**
16701          * @event beforeshow
16702          * Fires before the component is shown.  Return false to stop the show.
16703              * @param {Roo.Component} this
16704              */
16705         beforeshow : true,
16706         /**
16707          * @event show
16708          * Fires after the component is shown.
16709              * @param {Roo.Component} this
16710              */
16711         show : true,
16712         /**
16713          * @event beforehide
16714          * Fires before the component is hidden. Return false to stop the hide.
16715              * @param {Roo.Component} this
16716              */
16717         beforehide : true,
16718         /**
16719          * @event hide
16720          * Fires after the component is hidden.
16721              * @param {Roo.Component} this
16722              */
16723         hide : true,
16724         /**
16725          * @event beforerender
16726          * Fires before the component is rendered. Return false to stop the render.
16727              * @param {Roo.Component} this
16728              */
16729         beforerender : true,
16730         /**
16731          * @event render
16732          * Fires after the component is rendered.
16733              * @param {Roo.Component} this
16734              */
16735         render : true,
16736         /**
16737          * @event beforedestroy
16738          * Fires before the component is destroyed. Return false to stop the destroy.
16739              * @param {Roo.Component} this
16740              */
16741         beforedestroy : true,
16742         /**
16743          * @event destroy
16744          * Fires after the component is destroyed.
16745              * @param {Roo.Component} this
16746              */
16747         destroy : true
16748     });
16749     if(!this.id){
16750         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16751     }
16752     Roo.ComponentMgr.register(this);
16753     Roo.Component.superclass.constructor.call(this);
16754     this.initComponent();
16755     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16756         this.render(this.renderTo);
16757         delete this.renderTo;
16758     }
16759 };
16760
16761 /** @private */
16762 Roo.Component.AUTO_ID = 1000;
16763
16764 Roo.extend(Roo.Component, Roo.util.Observable, {
16765     /**
16766      * @scope Roo.Component.prototype
16767      * @type {Boolean}
16768      * true if this component is hidden. Read-only.
16769      */
16770     hidden : false,
16771     /**
16772      * @type {Boolean}
16773      * true if this component is disabled. Read-only.
16774      */
16775     disabled : false,
16776     /**
16777      * @type {Boolean}
16778      * true if this component has been rendered. Read-only.
16779      */
16780     rendered : false,
16781     
16782     /** @cfg {String} disableClass
16783      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16784      */
16785     disabledClass : "x-item-disabled",
16786         /** @cfg {Boolean} allowDomMove
16787          * Whether the component can move the Dom node when rendering (defaults to true).
16788          */
16789     allowDomMove : true,
16790     /** @cfg {String} hideMode (display|visibility)
16791      * How this component should hidden. Supported values are
16792      * "visibility" (css visibility), "offsets" (negative offset position) and
16793      * "display" (css display) - defaults to "display".
16794      */
16795     hideMode: 'display',
16796
16797     /** @private */
16798     ctype : "Roo.Component",
16799
16800     /**
16801      * @cfg {String} actionMode 
16802      * which property holds the element that used for  hide() / show() / disable() / enable()
16803      * default is 'el' for forms you probably want to set this to fieldEl 
16804      */
16805     actionMode : "el",
16806
16807     /** @private */
16808     getActionEl : function(){
16809         return this[this.actionMode];
16810     },
16811
16812     initComponent : Roo.emptyFn,
16813     /**
16814      * If this is a lazy rendering component, render it to its container element.
16815      * @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.
16816      */
16817     render : function(container, position){
16818         
16819         if(this.rendered){
16820             return this;
16821         }
16822         
16823         if(this.fireEvent("beforerender", this) === false){
16824             return false;
16825         }
16826         
16827         if(!container && this.el){
16828             this.el = Roo.get(this.el);
16829             container = this.el.dom.parentNode;
16830             this.allowDomMove = false;
16831         }
16832         this.container = Roo.get(container);
16833         this.rendered = true;
16834         if(position !== undefined){
16835             if(typeof position == 'number'){
16836                 position = this.container.dom.childNodes[position];
16837             }else{
16838                 position = Roo.getDom(position);
16839             }
16840         }
16841         this.onRender(this.container, position || null);
16842         if(this.cls){
16843             this.el.addClass(this.cls);
16844             delete this.cls;
16845         }
16846         if(this.style){
16847             this.el.applyStyles(this.style);
16848             delete this.style;
16849         }
16850         this.fireEvent("render", this);
16851         this.afterRender(this.container);
16852         if(this.hidden){
16853             this.hide();
16854         }
16855         if(this.disabled){
16856             this.disable();
16857         }
16858
16859         return this;
16860         
16861     },
16862
16863     /** @private */
16864     // default function is not really useful
16865     onRender : function(ct, position){
16866         if(this.el){
16867             this.el = Roo.get(this.el);
16868             if(this.allowDomMove !== false){
16869                 ct.dom.insertBefore(this.el.dom, position);
16870             }
16871         }
16872     },
16873
16874     /** @private */
16875     getAutoCreate : function(){
16876         var cfg = typeof this.autoCreate == "object" ?
16877                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16878         if(this.id && !cfg.id){
16879             cfg.id = this.id;
16880         }
16881         return cfg;
16882     },
16883
16884     /** @private */
16885     afterRender : Roo.emptyFn,
16886
16887     /**
16888      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16889      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16890      */
16891     destroy : function(){
16892         if(this.fireEvent("beforedestroy", this) !== false){
16893             this.purgeListeners();
16894             this.beforeDestroy();
16895             if(this.rendered){
16896                 this.el.removeAllListeners();
16897                 this.el.remove();
16898                 if(this.actionMode == "container"){
16899                     this.container.remove();
16900                 }
16901             }
16902             this.onDestroy();
16903             Roo.ComponentMgr.unregister(this);
16904             this.fireEvent("destroy", this);
16905         }
16906     },
16907
16908         /** @private */
16909     beforeDestroy : function(){
16910
16911     },
16912
16913         /** @private */
16914         onDestroy : function(){
16915
16916     },
16917
16918     /**
16919      * Returns the underlying {@link Roo.Element}.
16920      * @return {Roo.Element} The element
16921      */
16922     getEl : function(){
16923         return this.el;
16924     },
16925
16926     /**
16927      * Returns the id of this component.
16928      * @return {String}
16929      */
16930     getId : function(){
16931         return this.id;
16932     },
16933
16934     /**
16935      * Try to focus this component.
16936      * @param {Boolean} selectText True to also select the text in this component (if applicable)
16937      * @return {Roo.Component} this
16938      */
16939     focus : function(selectText){
16940         if(this.rendered){
16941             this.el.focus();
16942             if(selectText === true){
16943                 this.el.dom.select();
16944             }
16945         }
16946         return this;
16947     },
16948
16949     /** @private */
16950     blur : function(){
16951         if(this.rendered){
16952             this.el.blur();
16953         }
16954         return this;
16955     },
16956
16957     /**
16958      * Disable this component.
16959      * @return {Roo.Component} this
16960      */
16961     disable : function(){
16962         if(this.rendered){
16963             this.onDisable();
16964         }
16965         this.disabled = true;
16966         this.fireEvent("disable", this);
16967         return this;
16968     },
16969
16970         // private
16971     onDisable : function(){
16972         this.getActionEl().addClass(this.disabledClass);
16973         this.el.dom.disabled = true;
16974     },
16975
16976     /**
16977      * Enable this component.
16978      * @return {Roo.Component} this
16979      */
16980     enable : function(){
16981         if(this.rendered){
16982             this.onEnable();
16983         }
16984         this.disabled = false;
16985         this.fireEvent("enable", this);
16986         return this;
16987     },
16988
16989         // private
16990     onEnable : function(){
16991         this.getActionEl().removeClass(this.disabledClass);
16992         this.el.dom.disabled = false;
16993     },
16994
16995     /**
16996      * Convenience function for setting disabled/enabled by boolean.
16997      * @param {Boolean} disabled
16998      */
16999     setDisabled : function(disabled){
17000         this[disabled ? "disable" : "enable"]();
17001     },
17002
17003     /**
17004      * Show this component.
17005      * @return {Roo.Component} this
17006      */
17007     show: function(){
17008         if(this.fireEvent("beforeshow", this) !== false){
17009             this.hidden = false;
17010             if(this.rendered){
17011                 this.onShow();
17012             }
17013             this.fireEvent("show", this);
17014         }
17015         return this;
17016     },
17017
17018     // private
17019     onShow : function(){
17020         var ae = this.getActionEl();
17021         if(this.hideMode == 'visibility'){
17022             ae.dom.style.visibility = "visible";
17023         }else if(this.hideMode == 'offsets'){
17024             ae.removeClass('x-hidden');
17025         }else{
17026             ae.dom.style.display = "";
17027         }
17028     },
17029
17030     /**
17031      * Hide this component.
17032      * @return {Roo.Component} this
17033      */
17034     hide: function(){
17035         if(this.fireEvent("beforehide", this) !== false){
17036             this.hidden = true;
17037             if(this.rendered){
17038                 this.onHide();
17039             }
17040             this.fireEvent("hide", this);
17041         }
17042         return this;
17043     },
17044
17045     // private
17046     onHide : function(){
17047         var ae = this.getActionEl();
17048         if(this.hideMode == 'visibility'){
17049             ae.dom.style.visibility = "hidden";
17050         }else if(this.hideMode == 'offsets'){
17051             ae.addClass('x-hidden');
17052         }else{
17053             ae.dom.style.display = "none";
17054         }
17055     },
17056
17057     /**
17058      * Convenience function to hide or show this component by boolean.
17059      * @param {Boolean} visible True to show, false to hide
17060      * @return {Roo.Component} this
17061      */
17062     setVisible: function(visible){
17063         if(visible) {
17064             this.show();
17065         }else{
17066             this.hide();
17067         }
17068         return this;
17069     },
17070
17071     /**
17072      * Returns true if this component is visible.
17073      */
17074     isVisible : function(){
17075         return this.getActionEl().isVisible();
17076     },
17077
17078     cloneConfig : function(overrides){
17079         overrides = overrides || {};
17080         var id = overrides.id || Roo.id();
17081         var cfg = Roo.applyIf(overrides, this.initialConfig);
17082         cfg.id = id; // prevent dup id
17083         return new this.constructor(cfg);
17084     }
17085 });/*
17086  * Based on:
17087  * Ext JS Library 1.1.1
17088  * Copyright(c) 2006-2007, Ext JS, LLC.
17089  *
17090  * Originally Released Under LGPL - original licence link has changed is not relivant.
17091  *
17092  * Fork - LGPL
17093  * <script type="text/javascript">
17094  */
17095
17096 /**
17097  * @class Roo.BoxComponent
17098  * @extends Roo.Component
17099  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
17100  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
17101  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
17102  * layout containers.
17103  * @constructor
17104  * @param {Roo.Element/String/Object} config The configuration options.
17105  */
17106 Roo.BoxComponent = function(config){
17107     Roo.Component.call(this, config);
17108     this.addEvents({
17109         /**
17110          * @event resize
17111          * Fires after the component is resized.
17112              * @param {Roo.Component} this
17113              * @param {Number} adjWidth The box-adjusted width that was set
17114              * @param {Number} adjHeight The box-adjusted height that was set
17115              * @param {Number} rawWidth The width that was originally specified
17116              * @param {Number} rawHeight The height that was originally specified
17117              */
17118         resize : true,
17119         /**
17120          * @event move
17121          * Fires after the component is moved.
17122              * @param {Roo.Component} this
17123              * @param {Number} x The new x position
17124              * @param {Number} y The new y position
17125              */
17126         move : true
17127     });
17128 };
17129
17130 Roo.extend(Roo.BoxComponent, Roo.Component, {
17131     // private, set in afterRender to signify that the component has been rendered
17132     boxReady : false,
17133     // private, used to defer height settings to subclasses
17134     deferHeight: false,
17135     /** @cfg {Number} width
17136      * width (optional) size of component
17137      */
17138      /** @cfg {Number} height
17139      * height (optional) size of component
17140      */
17141      
17142     /**
17143      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
17144      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
17145      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
17146      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
17147      * @return {Roo.BoxComponent} this
17148      */
17149     setSize : function(w, h){
17150         // support for standard size objects
17151         if(typeof w == 'object'){
17152             h = w.height;
17153             w = w.width;
17154         }
17155         // not rendered
17156         if(!this.boxReady){
17157             this.width = w;
17158             this.height = h;
17159             return this;
17160         }
17161
17162         // prevent recalcs when not needed
17163         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17164             return this;
17165         }
17166         this.lastSize = {width: w, height: h};
17167
17168         var adj = this.adjustSize(w, h);
17169         var aw = adj.width, ah = adj.height;
17170         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17171             var rz = this.getResizeEl();
17172             if(!this.deferHeight && aw !== undefined && ah !== undefined){
17173                 rz.setSize(aw, ah);
17174             }else if(!this.deferHeight && ah !== undefined){
17175                 rz.setHeight(ah);
17176             }else if(aw !== undefined){
17177                 rz.setWidth(aw);
17178             }
17179             this.onResize(aw, ah, w, h);
17180             this.fireEvent('resize', this, aw, ah, w, h);
17181         }
17182         return this;
17183     },
17184
17185     /**
17186      * Gets the current size of the component's underlying element.
17187      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17188      */
17189     getSize : function(){
17190         return this.el.getSize();
17191     },
17192
17193     /**
17194      * Gets the current XY position of the component's underlying element.
17195      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17196      * @return {Array} The XY position of the element (e.g., [100, 200])
17197      */
17198     getPosition : function(local){
17199         if(local === true){
17200             return [this.el.getLeft(true), this.el.getTop(true)];
17201         }
17202         return this.xy || this.el.getXY();
17203     },
17204
17205     /**
17206      * Gets the current box measurements of the component's underlying element.
17207      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17208      * @returns {Object} box An object in the format {x, y, width, height}
17209      */
17210     getBox : function(local){
17211         var s = this.el.getSize();
17212         if(local){
17213             s.x = this.el.getLeft(true);
17214             s.y = this.el.getTop(true);
17215         }else{
17216             var xy = this.xy || this.el.getXY();
17217             s.x = xy[0];
17218             s.y = xy[1];
17219         }
17220         return s;
17221     },
17222
17223     /**
17224      * Sets the current box measurements of the component's underlying element.
17225      * @param {Object} box An object in the format {x, y, width, height}
17226      * @returns {Roo.BoxComponent} this
17227      */
17228     updateBox : function(box){
17229         this.setSize(box.width, box.height);
17230         this.setPagePosition(box.x, box.y);
17231         return this;
17232     },
17233
17234     // protected
17235     getResizeEl : function(){
17236         return this.resizeEl || this.el;
17237     },
17238
17239     // protected
17240     getPositionEl : function(){
17241         return this.positionEl || this.el;
17242     },
17243
17244     /**
17245      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
17246      * This method fires the move event.
17247      * @param {Number} left The new left
17248      * @param {Number} top The new top
17249      * @returns {Roo.BoxComponent} this
17250      */
17251     setPosition : function(x, y){
17252         this.x = x;
17253         this.y = y;
17254         if(!this.boxReady){
17255             return this;
17256         }
17257         var adj = this.adjustPosition(x, y);
17258         var ax = adj.x, ay = adj.y;
17259
17260         var el = this.getPositionEl();
17261         if(ax !== undefined || ay !== undefined){
17262             if(ax !== undefined && ay !== undefined){
17263                 el.setLeftTop(ax, ay);
17264             }else if(ax !== undefined){
17265                 el.setLeft(ax);
17266             }else if(ay !== undefined){
17267                 el.setTop(ay);
17268             }
17269             this.onPosition(ax, ay);
17270             this.fireEvent('move', this, ax, ay);
17271         }
17272         return this;
17273     },
17274
17275     /**
17276      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
17277      * This method fires the move event.
17278      * @param {Number} x The new x position
17279      * @param {Number} y The new y position
17280      * @returns {Roo.BoxComponent} this
17281      */
17282     setPagePosition : function(x, y){
17283         this.pageX = x;
17284         this.pageY = y;
17285         if(!this.boxReady){
17286             return;
17287         }
17288         if(x === undefined || y === undefined){ // cannot translate undefined points
17289             return;
17290         }
17291         var p = this.el.translatePoints(x, y);
17292         this.setPosition(p.left, p.top);
17293         return this;
17294     },
17295
17296     // private
17297     onRender : function(ct, position){
17298         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17299         if(this.resizeEl){
17300             this.resizeEl = Roo.get(this.resizeEl);
17301         }
17302         if(this.positionEl){
17303             this.positionEl = Roo.get(this.positionEl);
17304         }
17305     },
17306
17307     // private
17308     afterRender : function(){
17309         Roo.BoxComponent.superclass.afterRender.call(this);
17310         this.boxReady = true;
17311         this.setSize(this.width, this.height);
17312         if(this.x || this.y){
17313             this.setPosition(this.x, this.y);
17314         }
17315         if(this.pageX || this.pageY){
17316             this.setPagePosition(this.pageX, this.pageY);
17317         }
17318     },
17319
17320     /**
17321      * Force the component's size to recalculate based on the underlying element's current height and width.
17322      * @returns {Roo.BoxComponent} this
17323      */
17324     syncSize : function(){
17325         delete this.lastSize;
17326         this.setSize(this.el.getWidth(), this.el.getHeight());
17327         return this;
17328     },
17329
17330     /**
17331      * Called after the component is resized, this method is empty by default but can be implemented by any
17332      * subclass that needs to perform custom logic after a resize occurs.
17333      * @param {Number} adjWidth The box-adjusted width that was set
17334      * @param {Number} adjHeight The box-adjusted height that was set
17335      * @param {Number} rawWidth The width that was originally specified
17336      * @param {Number} rawHeight The height that was originally specified
17337      */
17338     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17339
17340     },
17341
17342     /**
17343      * Called after the component is moved, this method is empty by default but can be implemented by any
17344      * subclass that needs to perform custom logic after a move occurs.
17345      * @param {Number} x The new x position
17346      * @param {Number} y The new y position
17347      */
17348     onPosition : function(x, y){
17349
17350     },
17351
17352     // private
17353     adjustSize : function(w, h){
17354         if(this.autoWidth){
17355             w = 'auto';
17356         }
17357         if(this.autoHeight){
17358             h = 'auto';
17359         }
17360         return {width : w, height: h};
17361     },
17362
17363     // private
17364     adjustPosition : function(x, y){
17365         return {x : x, y: y};
17366     }
17367 });/*
17368  * Based on:
17369  * Ext JS Library 1.1.1
17370  * Copyright(c) 2006-2007, Ext JS, LLC.
17371  *
17372  * Originally Released Under LGPL - original licence link has changed is not relivant.
17373  *
17374  * Fork - LGPL
17375  * <script type="text/javascript">
17376  */
17377  (function(){ 
17378 /**
17379  * @class Roo.Layer
17380  * @extends Roo.Element
17381  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17382  * automatic maintaining of shadow/shim positions.
17383  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17384  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17385  * you can pass a string with a CSS class name. False turns off the shadow.
17386  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17387  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17388  * @cfg {String} cls CSS class to add to the element
17389  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17390  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17391  * @constructor
17392  * @param {Object} config An object with config options.
17393  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17394  */
17395
17396 Roo.Layer = function(config, existingEl){
17397     config = config || {};
17398     var dh = Roo.DomHelper;
17399     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17400     if(existingEl){
17401         this.dom = Roo.getDom(existingEl);
17402     }
17403     if(!this.dom){
17404         var o = config.dh || {tag: "div", cls: "x-layer"};
17405         this.dom = dh.append(pel, o);
17406     }
17407     if(config.cls){
17408         this.addClass(config.cls);
17409     }
17410     this.constrain = config.constrain !== false;
17411     this.visibilityMode = Roo.Element.VISIBILITY;
17412     if(config.id){
17413         this.id = this.dom.id = config.id;
17414     }else{
17415         this.id = Roo.id(this.dom);
17416     }
17417     this.zindex = config.zindex || this.getZIndex();
17418     this.position("absolute", this.zindex);
17419     if(config.shadow){
17420         this.shadowOffset = config.shadowOffset || 4;
17421         this.shadow = new Roo.Shadow({
17422             offset : this.shadowOffset,
17423             mode : config.shadow
17424         });
17425     }else{
17426         this.shadowOffset = 0;
17427     }
17428     this.useShim = config.shim !== false && Roo.useShims;
17429     this.useDisplay = config.useDisplay;
17430     this.hide();
17431 };
17432
17433 var supr = Roo.Element.prototype;
17434
17435 // shims are shared among layer to keep from having 100 iframes
17436 var shims = [];
17437
17438 Roo.extend(Roo.Layer, Roo.Element, {
17439
17440     getZIndex : function(){
17441         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17442     },
17443
17444     getShim : function(){
17445         if(!this.useShim){
17446             return null;
17447         }
17448         if(this.shim){
17449             return this.shim;
17450         }
17451         var shim = shims.shift();
17452         if(!shim){
17453             shim = this.createShim();
17454             shim.enableDisplayMode('block');
17455             shim.dom.style.display = 'none';
17456             shim.dom.style.visibility = 'visible';
17457         }
17458         var pn = this.dom.parentNode;
17459         if(shim.dom.parentNode != pn){
17460             pn.insertBefore(shim.dom, this.dom);
17461         }
17462         shim.setStyle('z-index', this.getZIndex()-2);
17463         this.shim = shim;
17464         return shim;
17465     },
17466
17467     hideShim : function(){
17468         if(this.shim){
17469             this.shim.setDisplayed(false);
17470             shims.push(this.shim);
17471             delete this.shim;
17472         }
17473     },
17474
17475     disableShadow : function(){
17476         if(this.shadow){
17477             this.shadowDisabled = true;
17478             this.shadow.hide();
17479             this.lastShadowOffset = this.shadowOffset;
17480             this.shadowOffset = 0;
17481         }
17482     },
17483
17484     enableShadow : function(show){
17485         if(this.shadow){
17486             this.shadowDisabled = false;
17487             this.shadowOffset = this.lastShadowOffset;
17488             delete this.lastShadowOffset;
17489             if(show){
17490                 this.sync(true);
17491             }
17492         }
17493     },
17494
17495     // private
17496     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17497     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17498     sync : function(doShow){
17499         var sw = this.shadow;
17500         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17501             var sh = this.getShim();
17502
17503             var w = this.getWidth(),
17504                 h = this.getHeight();
17505
17506             var l = this.getLeft(true),
17507                 t = this.getTop(true);
17508
17509             if(sw && !this.shadowDisabled){
17510                 if(doShow && !sw.isVisible()){
17511                     sw.show(this);
17512                 }else{
17513                     sw.realign(l, t, w, h);
17514                 }
17515                 if(sh){
17516                     if(doShow){
17517                        sh.show();
17518                     }
17519                     // fit the shim behind the shadow, so it is shimmed too
17520                     var a = sw.adjusts, s = sh.dom.style;
17521                     s.left = (Math.min(l, l+a.l))+"px";
17522                     s.top = (Math.min(t, t+a.t))+"px";
17523                     s.width = (w+a.w)+"px";
17524                     s.height = (h+a.h)+"px";
17525                 }
17526             }else if(sh){
17527                 if(doShow){
17528                    sh.show();
17529                 }
17530                 sh.setSize(w, h);
17531                 sh.setLeftTop(l, t);
17532             }
17533             
17534         }
17535     },
17536
17537     // private
17538     destroy : function(){
17539         this.hideShim();
17540         if(this.shadow){
17541             this.shadow.hide();
17542         }
17543         this.removeAllListeners();
17544         var pn = this.dom.parentNode;
17545         if(pn){
17546             pn.removeChild(this.dom);
17547         }
17548         Roo.Element.uncache(this.id);
17549     },
17550
17551     remove : function(){
17552         this.destroy();
17553     },
17554
17555     // private
17556     beginUpdate : function(){
17557         this.updating = true;
17558     },
17559
17560     // private
17561     endUpdate : function(){
17562         this.updating = false;
17563         this.sync(true);
17564     },
17565
17566     // private
17567     hideUnders : function(negOffset){
17568         if(this.shadow){
17569             this.shadow.hide();
17570         }
17571         this.hideShim();
17572     },
17573
17574     // private
17575     constrainXY : function(){
17576         if(this.constrain){
17577             var vw = Roo.lib.Dom.getViewWidth(),
17578                 vh = Roo.lib.Dom.getViewHeight();
17579             var s = Roo.get(document).getScroll();
17580
17581             var xy = this.getXY();
17582             var x = xy[0], y = xy[1];   
17583             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17584             // only move it if it needs it
17585             var moved = false;
17586             // first validate right/bottom
17587             if((x + w) > vw+s.left){
17588                 x = vw - w - this.shadowOffset;
17589                 moved = true;
17590             }
17591             if((y + h) > vh+s.top){
17592                 y = vh - h - this.shadowOffset;
17593                 moved = true;
17594             }
17595             // then make sure top/left isn't negative
17596             if(x < s.left){
17597                 x = s.left;
17598                 moved = true;
17599             }
17600             if(y < s.top){
17601                 y = s.top;
17602                 moved = true;
17603             }
17604             if(moved){
17605                 if(this.avoidY){
17606                     var ay = this.avoidY;
17607                     if(y <= ay && (y+h) >= ay){
17608                         y = ay-h-5;   
17609                     }
17610                 }
17611                 xy = [x, y];
17612                 this.storeXY(xy);
17613                 supr.setXY.call(this, xy);
17614                 this.sync();
17615             }
17616         }
17617     },
17618
17619     isVisible : function(){
17620         return this.visible;    
17621     },
17622
17623     // private
17624     showAction : function(){
17625         this.visible = true; // track visibility to prevent getStyle calls
17626         if(this.useDisplay === true){
17627             this.setDisplayed("");
17628         }else if(this.lastXY){
17629             supr.setXY.call(this, this.lastXY);
17630         }else if(this.lastLT){
17631             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17632         }
17633     },
17634
17635     // private
17636     hideAction : function(){
17637         this.visible = false;
17638         if(this.useDisplay === true){
17639             this.setDisplayed(false);
17640         }else{
17641             this.setLeftTop(-10000,-10000);
17642         }
17643     },
17644
17645     // overridden Element method
17646     setVisible : function(v, a, d, c, e){
17647         if(v){
17648             this.showAction();
17649         }
17650         if(a && v){
17651             var cb = function(){
17652                 this.sync(true);
17653                 if(c){
17654                     c();
17655                 }
17656             }.createDelegate(this);
17657             supr.setVisible.call(this, true, true, d, cb, e);
17658         }else{
17659             if(!v){
17660                 this.hideUnders(true);
17661             }
17662             var cb = c;
17663             if(a){
17664                 cb = function(){
17665                     this.hideAction();
17666                     if(c){
17667                         c();
17668                     }
17669                 }.createDelegate(this);
17670             }
17671             supr.setVisible.call(this, v, a, d, cb, e);
17672             if(v){
17673                 this.sync(true);
17674             }else if(!a){
17675                 this.hideAction();
17676             }
17677         }
17678     },
17679
17680     storeXY : function(xy){
17681         delete this.lastLT;
17682         this.lastXY = xy;
17683     },
17684
17685     storeLeftTop : function(left, top){
17686         delete this.lastXY;
17687         this.lastLT = [left, top];
17688     },
17689
17690     // private
17691     beforeFx : function(){
17692         this.beforeAction();
17693         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17694     },
17695
17696     // private
17697     afterFx : function(){
17698         Roo.Layer.superclass.afterFx.apply(this, arguments);
17699         this.sync(this.isVisible());
17700     },
17701
17702     // private
17703     beforeAction : function(){
17704         if(!this.updating && this.shadow){
17705             this.shadow.hide();
17706         }
17707     },
17708
17709     // overridden Element method
17710     setLeft : function(left){
17711         this.storeLeftTop(left, this.getTop(true));
17712         supr.setLeft.apply(this, arguments);
17713         this.sync();
17714     },
17715
17716     setTop : function(top){
17717         this.storeLeftTop(this.getLeft(true), top);
17718         supr.setTop.apply(this, arguments);
17719         this.sync();
17720     },
17721
17722     setLeftTop : function(left, top){
17723         this.storeLeftTop(left, top);
17724         supr.setLeftTop.apply(this, arguments);
17725         this.sync();
17726     },
17727
17728     setXY : function(xy, a, d, c, e){
17729         this.fixDisplay();
17730         this.beforeAction();
17731         this.storeXY(xy);
17732         var cb = this.createCB(c);
17733         supr.setXY.call(this, xy, a, d, cb, e);
17734         if(!a){
17735             cb();
17736         }
17737     },
17738
17739     // private
17740     createCB : function(c){
17741         var el = this;
17742         return function(){
17743             el.constrainXY();
17744             el.sync(true);
17745             if(c){
17746                 c();
17747             }
17748         };
17749     },
17750
17751     // overridden Element method
17752     setX : function(x, a, d, c, e){
17753         this.setXY([x, this.getY()], a, d, c, e);
17754     },
17755
17756     // overridden Element method
17757     setY : function(y, a, d, c, e){
17758         this.setXY([this.getX(), y], a, d, c, e);
17759     },
17760
17761     // overridden Element method
17762     setSize : function(w, h, a, d, c, e){
17763         this.beforeAction();
17764         var cb = this.createCB(c);
17765         supr.setSize.call(this, w, h, a, d, cb, e);
17766         if(!a){
17767             cb();
17768         }
17769     },
17770
17771     // overridden Element method
17772     setWidth : function(w, a, d, c, e){
17773         this.beforeAction();
17774         var cb = this.createCB(c);
17775         supr.setWidth.call(this, w, a, d, cb, e);
17776         if(!a){
17777             cb();
17778         }
17779     },
17780
17781     // overridden Element method
17782     setHeight : function(h, a, d, c, e){
17783         this.beforeAction();
17784         var cb = this.createCB(c);
17785         supr.setHeight.call(this, h, a, d, cb, e);
17786         if(!a){
17787             cb();
17788         }
17789     },
17790
17791     // overridden Element method
17792     setBounds : function(x, y, w, h, a, d, c, e){
17793         this.beforeAction();
17794         var cb = this.createCB(c);
17795         if(!a){
17796             this.storeXY([x, y]);
17797             supr.setXY.call(this, [x, y]);
17798             supr.setSize.call(this, w, h, a, d, cb, e);
17799             cb();
17800         }else{
17801             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17802         }
17803         return this;
17804     },
17805     
17806     /**
17807      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17808      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17809      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17810      * @param {Number} zindex The new z-index to set
17811      * @return {this} The Layer
17812      */
17813     setZIndex : function(zindex){
17814         this.zindex = zindex;
17815         this.setStyle("z-index", zindex + 2);
17816         if(this.shadow){
17817             this.shadow.setZIndex(zindex + 1);
17818         }
17819         if(this.shim){
17820             this.shim.setStyle("z-index", zindex);
17821         }
17822     }
17823 });
17824 })();/*
17825  * Original code for Roojs - LGPL
17826  * <script type="text/javascript">
17827  */
17828  
17829 /**
17830  * @class Roo.XComponent
17831  * A delayed Element creator...
17832  * Or a way to group chunks of interface together.
17833  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17834  *  used in conjunction with XComponent.build() it will create an instance of each element,
17835  *  then call addxtype() to build the User interface.
17836  * 
17837  * Mypart.xyx = new Roo.XComponent({
17838
17839     parent : 'Mypart.xyz', // empty == document.element.!!
17840     order : '001',
17841     name : 'xxxx'
17842     region : 'xxxx'
17843     disabled : function() {} 
17844      
17845     tree : function() { // return an tree of xtype declared components
17846         var MODULE = this;
17847         return 
17848         {
17849             xtype : 'NestedLayoutPanel',
17850             // technicall
17851         }
17852      ]
17853  *})
17854  *
17855  *
17856  * It can be used to build a big heiracy, with parent etc.
17857  * or you can just use this to render a single compoent to a dom element
17858  * MYPART.render(Roo.Element | String(id) | dom_element )
17859  *
17860  *
17861  * Usage patterns.
17862  *
17863  * Classic Roo
17864  *
17865  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17866  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17867  *
17868  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17869  *
17870  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17871  * - if mulitple topModules exist, the last one is defined as the top module.
17872  *
17873  * Embeded Roo
17874  * 
17875  * When the top level or multiple modules are to embedded into a existing HTML page,
17876  * the parent element can container '#id' of the element where the module will be drawn.
17877  *
17878  * Bootstrap Roo
17879  *
17880  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17881  * it relies more on a include mechanism, where sub modules are included into an outer page.
17882  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17883  * 
17884  * Bootstrap Roo Included elements
17885  *
17886  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17887  * hence confusing the component builder as it thinks there are multiple top level elements. 
17888  *
17889  * String Over-ride & Translations
17890  *
17891  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17892  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17893  * are needed. @see Roo.XComponent.overlayString  
17894  * 
17895  * 
17896  * 
17897  * @extends Roo.util.Observable
17898  * @constructor
17899  * @param cfg {Object} configuration of component
17900  * 
17901  */
17902 Roo.XComponent = function(cfg) {
17903     Roo.apply(this, cfg);
17904     this.addEvents({ 
17905         /**
17906              * @event built
17907              * Fires when this the componnt is built
17908              * @param {Roo.XComponent} c the component
17909              */
17910         'built' : true
17911         
17912     });
17913     this.region = this.region || 'center'; // default..
17914     Roo.XComponent.register(this);
17915     this.modules = false;
17916     this.el = false; // where the layout goes..
17917     
17918     
17919 }
17920 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17921     /**
17922      * @property el
17923      * The created element (with Roo.factory())
17924      * @type {Roo.Layout}
17925      */
17926     el  : false,
17927     
17928     /**
17929      * @property el
17930      * for BC  - use el in new code
17931      * @type {Roo.Layout}
17932      */
17933     panel : false,
17934     
17935     /**
17936      * @property layout
17937      * for BC  - use el in new code
17938      * @type {Roo.Layout}
17939      */
17940     layout : false,
17941     
17942      /**
17943      * @cfg {Function|boolean} disabled
17944      * If this module is disabled by some rule, return true from the funtion
17945      */
17946     disabled : false,
17947     
17948     /**
17949      * @cfg {String} parent 
17950      * Name of parent element which it get xtype added to..
17951      */
17952     parent: false,
17953     
17954     /**
17955      * @cfg {String} order
17956      * Used to set the order in which elements are created (usefull for multiple tabs)
17957      */
17958     
17959     order : false,
17960     /**
17961      * @cfg {String} name
17962      * String to display while loading.
17963      */
17964     name : false,
17965     /**
17966      * @cfg {String} region
17967      * Region to render component to (defaults to center)
17968      */
17969     region : 'center',
17970     
17971     /**
17972      * @cfg {Array} items
17973      * A single item array - the first element is the root of the tree..
17974      * It's done this way to stay compatible with the Xtype system...
17975      */
17976     items : false,
17977     
17978     /**
17979      * @property _tree
17980      * The method that retuns the tree of parts that make up this compoennt 
17981      * @type {function}
17982      */
17983     _tree  : false,
17984     
17985      /**
17986      * render
17987      * render element to dom or tree
17988      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17989      */
17990     
17991     render : function(el)
17992     {
17993         
17994         el = el || false;
17995         var hp = this.parent ? 1 : 0;
17996         Roo.debug &&  Roo.log(this);
17997         
17998         var tree = this._tree ? this._tree() : this.tree();
17999
18000         
18001         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
18002             // if parent is a '#.....' string, then let's use that..
18003             var ename = this.parent.substr(1);
18004             this.parent = false;
18005             Roo.debug && Roo.log(ename);
18006             switch (ename) {
18007                 case 'bootstrap-body':
18008                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
18009                         // this is the BorderLayout standard?
18010                        this.parent = { el : true };
18011                        break;
18012                     }
18013                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
18014                         // need to insert stuff...
18015                         this.parent =  {
18016                              el : new Roo.bootstrap.layout.Border({
18017                                  el : document.body, 
18018                      
18019                                  center: {
18020                                     titlebar: false,
18021                                     autoScroll:false,
18022                                     closeOnTab: true,
18023                                     tabPosition: 'top',
18024                                       //resizeTabs: true,
18025                                     alwaysShowTabs: true,
18026                                     hideTabs: false
18027                                      //minTabWidth: 140
18028                                  }
18029                              })
18030                         
18031                          };
18032                          break;
18033                     }
18034                          
18035                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
18036                         this.parent = { el :  new  Roo.bootstrap.Body() };
18037                         Roo.debug && Roo.log("setting el to doc body");
18038                          
18039                     } else {
18040                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
18041                     }
18042                     break;
18043                 case 'bootstrap':
18044                     this.parent = { el : true};
18045                     // fall through
18046                 default:
18047                     el = Roo.get(ename);
18048                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
18049                         this.parent = { el : true};
18050                     }
18051                     
18052                     break;
18053             }
18054                 
18055             
18056             if (!el && !this.parent) {
18057                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
18058                 return;
18059             }
18060         }
18061         
18062         Roo.debug && Roo.log("EL:");
18063         Roo.debug && Roo.log(el);
18064         Roo.debug && Roo.log("this.parent.el:");
18065         Roo.debug && Roo.log(this.parent.el);
18066         
18067
18068         // altertive root elements ??? - we need a better way to indicate these.
18069         var is_alt = Roo.XComponent.is_alt ||
18070                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
18071                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
18072                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
18073         
18074         
18075         
18076         if (!this.parent && is_alt) {
18077             //el = Roo.get(document.body);
18078             this.parent = { el : true };
18079         }
18080             
18081             
18082         
18083         if (!this.parent) {
18084             
18085             Roo.debug && Roo.log("no parent - creating one");
18086             
18087             el = el ? Roo.get(el) : false;      
18088             
18089             if (typeof(Roo.BorderLayout) == 'undefined' ) {
18090                 
18091                 this.parent =  {
18092                     el : new Roo.bootstrap.layout.Border({
18093                         el: el || document.body,
18094                     
18095                         center: {
18096                             titlebar: false,
18097                             autoScroll:false,
18098                             closeOnTab: true,
18099                             tabPosition: 'top',
18100                              //resizeTabs: true,
18101                             alwaysShowTabs: false,
18102                             hideTabs: true,
18103                             minTabWidth: 140,
18104                             overflow: 'visible'
18105                          }
18106                      })
18107                 };
18108             } else {
18109             
18110                 // it's a top level one..
18111                 this.parent =  {
18112                     el : new Roo.BorderLayout(el || document.body, {
18113                         center: {
18114                             titlebar: false,
18115                             autoScroll:false,
18116                             closeOnTab: true,
18117                             tabPosition: 'top',
18118                              //resizeTabs: true,
18119                             alwaysShowTabs: el && hp? false :  true,
18120                             hideTabs: el || !hp ? true :  false,
18121                             minTabWidth: 140
18122                          }
18123                     })
18124                 };
18125             }
18126         }
18127         
18128         if (!this.parent.el) {
18129                 // probably an old style ctor, which has been disabled.
18130                 return;
18131
18132         }
18133                 // The 'tree' method is  '_tree now' 
18134             
18135         tree.region = tree.region || this.region;
18136         var is_body = false;
18137         if (this.parent.el === true) {
18138             // bootstrap... - body..
18139             if (el) {
18140                 tree.el = el;
18141             }
18142             this.parent.el = Roo.factory(tree);
18143             is_body = true;
18144         }
18145         
18146         this.el = this.parent.el.addxtype(tree, undefined, is_body);
18147         this.fireEvent('built', this);
18148         
18149         this.panel = this.el;
18150         this.layout = this.panel.layout;
18151         this.parentLayout = this.parent.layout  || false;  
18152          
18153     }
18154     
18155 });
18156
18157 Roo.apply(Roo.XComponent, {
18158     /**
18159      * @property  hideProgress
18160      * true to disable the building progress bar.. usefull on single page renders.
18161      * @type Boolean
18162      */
18163     hideProgress : false,
18164     /**
18165      * @property  buildCompleted
18166      * True when the builder has completed building the interface.
18167      * @type Boolean
18168      */
18169     buildCompleted : false,
18170      
18171     /**
18172      * @property  topModule
18173      * the upper most module - uses document.element as it's constructor.
18174      * @type Object
18175      */
18176      
18177     topModule  : false,
18178       
18179     /**
18180      * @property  modules
18181      * array of modules to be created by registration system.
18182      * @type {Array} of Roo.XComponent
18183      */
18184     
18185     modules : [],
18186     /**
18187      * @property  elmodules
18188      * array of modules to be created by which use #ID 
18189      * @type {Array} of Roo.XComponent
18190      */
18191      
18192     elmodules : [],
18193
18194      /**
18195      * @property  is_alt
18196      * Is an alternative Root - normally used by bootstrap or other systems,
18197      *    where the top element in the tree can wrap 'body' 
18198      * @type {boolean}  (default false)
18199      */
18200      
18201     is_alt : false,
18202     /**
18203      * @property  build_from_html
18204      * Build elements from html - used by bootstrap HTML stuff 
18205      *    - this is cleared after build is completed
18206      * @type {boolean}    (default false)
18207      */
18208      
18209     build_from_html : false,
18210     /**
18211      * Register components to be built later.
18212      *
18213      * This solves the following issues
18214      * - Building is not done on page load, but after an authentication process has occured.
18215      * - Interface elements are registered on page load
18216      * - Parent Interface elements may not be loaded before child, so this handles that..
18217      * 
18218      *
18219      * example:
18220      * 
18221      * MyApp.register({
18222           order : '000001',
18223           module : 'Pman.Tab.projectMgr',
18224           region : 'center',
18225           parent : 'Pman.layout',
18226           disabled : false,  // or use a function..
18227         })
18228      
18229      * * @param {Object} details about module
18230      */
18231     register : function(obj) {
18232                 
18233         Roo.XComponent.event.fireEvent('register', obj);
18234         switch(typeof(obj.disabled) ) {
18235                 
18236             case 'undefined':
18237                 break;
18238             
18239             case 'function':
18240                 if ( obj.disabled() ) {
18241                         return;
18242                 }
18243                 break;
18244             
18245             default:
18246                 if (obj.disabled || obj.region == '#disabled') {
18247                         return;
18248                 }
18249                 break;
18250         }
18251                 
18252         this.modules.push(obj);
18253          
18254     },
18255     /**
18256      * convert a string to an object..
18257      * eg. 'AAA.BBB' -> finds AAA.BBB
18258
18259      */
18260     
18261     toObject : function(str)
18262     {
18263         if (!str || typeof(str) == 'object') {
18264             return str;
18265         }
18266         if (str.substring(0,1) == '#') {
18267             return str;
18268         }
18269
18270         var ar = str.split('.');
18271         var rt, o;
18272         rt = ar.shift();
18273             /** eval:var:o */
18274         try {
18275             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
18276         } catch (e) {
18277             throw "Module not found : " + str;
18278         }
18279         
18280         if (o === false) {
18281             throw "Module not found : " + str;
18282         }
18283         Roo.each(ar, function(e) {
18284             if (typeof(o[e]) == 'undefined') {
18285                 throw "Module not found : " + str;
18286             }
18287             o = o[e];
18288         });
18289         
18290         return o;
18291         
18292     },
18293     
18294     
18295     /**
18296      * move modules into their correct place in the tree..
18297      * 
18298      */
18299     preBuild : function ()
18300     {
18301         var _t = this;
18302         Roo.each(this.modules , function (obj)
18303         {
18304             Roo.XComponent.event.fireEvent('beforebuild', obj);
18305             
18306             var opar = obj.parent;
18307             try { 
18308                 obj.parent = this.toObject(opar);
18309             } catch(e) {
18310                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18311                 return;
18312             }
18313             
18314             if (!obj.parent) {
18315                 Roo.debug && Roo.log("GOT top level module");
18316                 Roo.debug && Roo.log(obj);
18317                 obj.modules = new Roo.util.MixedCollection(false, 
18318                     function(o) { return o.order + '' }
18319                 );
18320                 this.topModule = obj;
18321                 return;
18322             }
18323                         // parent is a string (usually a dom element name..)
18324             if (typeof(obj.parent) == 'string') {
18325                 this.elmodules.push(obj);
18326                 return;
18327             }
18328             if (obj.parent.constructor != Roo.XComponent) {
18329                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18330             }
18331             if (!obj.parent.modules) {
18332                 obj.parent.modules = new Roo.util.MixedCollection(false, 
18333                     function(o) { return o.order + '' }
18334                 );
18335             }
18336             if (obj.parent.disabled) {
18337                 obj.disabled = true;
18338             }
18339             obj.parent.modules.add(obj);
18340         }, this);
18341     },
18342     
18343      /**
18344      * make a list of modules to build.
18345      * @return {Array} list of modules. 
18346      */ 
18347     
18348     buildOrder : function()
18349     {
18350         var _this = this;
18351         var cmp = function(a,b) {   
18352             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18353         };
18354         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18355             throw "No top level modules to build";
18356         }
18357         
18358         // make a flat list in order of modules to build.
18359         var mods = this.topModule ? [ this.topModule ] : [];
18360                 
18361         
18362         // elmodules (is a list of DOM based modules )
18363         Roo.each(this.elmodules, function(e) {
18364             mods.push(e);
18365             if (!this.topModule &&
18366                 typeof(e.parent) == 'string' &&
18367                 e.parent.substring(0,1) == '#' &&
18368                 Roo.get(e.parent.substr(1))
18369                ) {
18370                 
18371                 _this.topModule = e;
18372             }
18373             
18374         });
18375
18376         
18377         // add modules to their parents..
18378         var addMod = function(m) {
18379             Roo.debug && Roo.log("build Order: add: " + m.name);
18380                 
18381             mods.push(m);
18382             if (m.modules && !m.disabled) {
18383                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18384                 m.modules.keySort('ASC',  cmp );
18385                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18386     
18387                 m.modules.each(addMod);
18388             } else {
18389                 Roo.debug && Roo.log("build Order: no child modules");
18390             }
18391             // not sure if this is used any more..
18392             if (m.finalize) {
18393                 m.finalize.name = m.name + " (clean up) ";
18394                 mods.push(m.finalize);
18395             }
18396             
18397         }
18398         if (this.topModule && this.topModule.modules) { 
18399             this.topModule.modules.keySort('ASC',  cmp );
18400             this.topModule.modules.each(addMod);
18401         } 
18402         return mods;
18403     },
18404     
18405      /**
18406      * Build the registered modules.
18407      * @param {Object} parent element.
18408      * @param {Function} optional method to call after module has been added.
18409      * 
18410      */ 
18411    
18412     build : function(opts) 
18413     {
18414         
18415         if (typeof(opts) != 'undefined') {
18416             Roo.apply(this,opts);
18417         }
18418         
18419         this.preBuild();
18420         var mods = this.buildOrder();
18421       
18422         //this.allmods = mods;
18423         //Roo.debug && Roo.log(mods);
18424         //return;
18425         if (!mods.length) { // should not happen
18426             throw "NO modules!!!";
18427         }
18428         
18429         
18430         var msg = "Building Interface...";
18431         // flash it up as modal - so we store the mask!?
18432         if (!this.hideProgress && Roo.MessageBox) {
18433             Roo.MessageBox.show({ title: 'loading' });
18434             Roo.MessageBox.show({
18435                title: "Please wait...",
18436                msg: msg,
18437                width:450,
18438                progress:true,
18439                buttons : false,
18440                closable:false,
18441                modal: false
18442               
18443             });
18444         }
18445         var total = mods.length;
18446         
18447         var _this = this;
18448         var progressRun = function() {
18449             if (!mods.length) {
18450                 Roo.debug && Roo.log('hide?');
18451                 if (!this.hideProgress && Roo.MessageBox) {
18452                     Roo.MessageBox.hide();
18453                 }
18454                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18455                 
18456                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18457                 
18458                 // THE END...
18459                 return false;   
18460             }
18461             
18462             var m = mods.shift();
18463             
18464             
18465             Roo.debug && Roo.log(m);
18466             // not sure if this is supported any more.. - modules that are are just function
18467             if (typeof(m) == 'function') { 
18468                 m.call(this);
18469                 return progressRun.defer(10, _this);
18470             } 
18471             
18472             
18473             msg = "Building Interface " + (total  - mods.length) + 
18474                     " of " + total + 
18475                     (m.name ? (' - ' + m.name) : '');
18476                         Roo.debug && Roo.log(msg);
18477             if (!_this.hideProgress &&  Roo.MessageBox) { 
18478                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18479             }
18480             
18481          
18482             // is the module disabled?
18483             var disabled = (typeof(m.disabled) == 'function') ?
18484                 m.disabled.call(m.module.disabled) : m.disabled;    
18485             
18486             
18487             if (disabled) {
18488                 return progressRun(); // we do not update the display!
18489             }
18490             
18491             // now build 
18492             
18493                         
18494                         
18495             m.render();
18496             // it's 10 on top level, and 1 on others??? why...
18497             return progressRun.defer(10, _this);
18498              
18499         }
18500         progressRun.defer(1, _this);
18501      
18502         
18503         
18504     },
18505     /**
18506      * Overlay a set of modified strings onto a component
18507      * This is dependant on our builder exporting the strings and 'named strings' elements.
18508      * 
18509      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18510      * @param {Object} associative array of 'named' string and it's new value.
18511      * 
18512      */
18513         overlayStrings : function( component, strings )
18514     {
18515         if (typeof(component['_named_strings']) == 'undefined') {
18516             throw "ERROR: component does not have _named_strings";
18517         }
18518         for ( var k in strings ) {
18519             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18520             if (md !== false) {
18521                 component['_strings'][md] = strings[k];
18522             } else {
18523                 Roo.log('could not find named string: ' + k + ' in');
18524                 Roo.log(component);
18525             }
18526             
18527         }
18528         
18529     },
18530     
18531         
18532         /**
18533          * Event Object.
18534          *
18535          *
18536          */
18537         event: false, 
18538     /**
18539          * wrapper for event.on - aliased later..  
18540          * Typically use to register a event handler for register:
18541          *
18542          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18543          *
18544          */
18545     on : false
18546    
18547     
18548     
18549 });
18550
18551 Roo.XComponent.event = new Roo.util.Observable({
18552                 events : { 
18553                         /**
18554                          * @event register
18555                          * Fires when an Component is registered,
18556                          * set the disable property on the Component to stop registration.
18557                          * @param {Roo.XComponent} c the component being registerd.
18558                          * 
18559                          */
18560                         'register' : true,
18561             /**
18562                          * @event beforebuild
18563                          * Fires before each Component is built
18564                          * can be used to apply permissions.
18565                          * @param {Roo.XComponent} c the component being registerd.
18566                          * 
18567                          */
18568                         'beforebuild' : true,
18569                         /**
18570                          * @event buildcomplete
18571                          * Fires on the top level element when all elements have been built
18572                          * @param {Roo.XComponent} the top level component.
18573                          */
18574                         'buildcomplete' : true
18575                         
18576                 }
18577 });
18578
18579 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18580  //
18581  /**
18582  * marked - a markdown parser
18583  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18584  * https://github.com/chjj/marked
18585  */
18586
18587
18588 /**
18589  *
18590  * Roo.Markdown - is a very crude wrapper around marked..
18591  *
18592  * usage:
18593  * 
18594  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18595  * 
18596  * Note: move the sample code to the bottom of this
18597  * file before uncommenting it.
18598  *
18599  */
18600
18601 Roo.Markdown = {};
18602 Roo.Markdown.toHtml = function(text) {
18603     
18604     var c = new Roo.Markdown.marked.setOptions({
18605             renderer: new Roo.Markdown.marked.Renderer(),
18606             gfm: true,
18607             tables: true,
18608             breaks: false,
18609             pedantic: false,
18610             sanitize: false,
18611             smartLists: true,
18612             smartypants: false
18613           });
18614     // A FEW HACKS!!?
18615     
18616     text = text.replace(/\\\n/g,' ');
18617     return Roo.Markdown.marked(text);
18618 };
18619 //
18620 // converter
18621 //
18622 // Wraps all "globals" so that the only thing
18623 // exposed is makeHtml().
18624 //
18625 (function() {
18626     
18627      /**
18628          * eval:var:escape
18629          * eval:var:unescape
18630          * eval:var:replace
18631          */
18632       
18633     /**
18634      * Helpers
18635      */
18636     
18637     var escape = function (html, encode) {
18638       return html
18639         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18640         .replace(/</g, '&lt;')
18641         .replace(/>/g, '&gt;')
18642         .replace(/"/g, '&quot;')
18643         .replace(/'/g, '&#39;');
18644     }
18645     
18646     var unescape = function (html) {
18647         // explicitly match decimal, hex, and named HTML entities 
18648       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18649         n = n.toLowerCase();
18650         if (n === 'colon') { return ':'; }
18651         if (n.charAt(0) === '#') {
18652           return n.charAt(1) === 'x'
18653             ? String.fromCharCode(parseInt(n.substring(2), 16))
18654             : String.fromCharCode(+n.substring(1));
18655         }
18656         return '';
18657       });
18658     }
18659     
18660     var replace = function (regex, opt) {
18661       regex = regex.source;
18662       opt = opt || '';
18663       return function self(name, val) {
18664         if (!name) { return new RegExp(regex, opt); }
18665         val = val.source || val;
18666         val = val.replace(/(^|[^\[])\^/g, '$1');
18667         regex = regex.replace(name, val);
18668         return self;
18669       };
18670     }
18671
18672
18673          /**
18674          * eval:var:noop
18675     */
18676     var noop = function () {}
18677     noop.exec = noop;
18678     
18679          /**
18680          * eval:var:merge
18681     */
18682     var merge = function (obj) {
18683       var i = 1
18684         , target
18685         , key;
18686     
18687       for (; i < arguments.length; i++) {
18688         target = arguments[i];
18689         for (key in target) {
18690           if (Object.prototype.hasOwnProperty.call(target, key)) {
18691             obj[key] = target[key];
18692           }
18693         }
18694       }
18695     
18696       return obj;
18697     }
18698     
18699     
18700     /**
18701      * Block-Level Grammar
18702      */
18703     
18704     
18705     
18706     
18707     var block = {
18708       newline: /^\n+/,
18709       code: /^( {4}[^\n]+\n*)+/,
18710       fences: noop,
18711       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18712       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18713       nptable: noop,
18714       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18715       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18716       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18717       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18718       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18719       table: noop,
18720       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18721       text: /^[^\n]+/
18722     };
18723     
18724     block.bullet = /(?:[*+-]|\d+\.)/;
18725     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18726     block.item = replace(block.item, 'gm')
18727       (/bull/g, block.bullet)
18728       ();
18729     
18730     block.list = replace(block.list)
18731       (/bull/g, block.bullet)
18732       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18733       ('def', '\\n+(?=' + block.def.source + ')')
18734       ();
18735     
18736     block.blockquote = replace(block.blockquote)
18737       ('def', block.def)
18738       ();
18739     
18740     block._tag = '(?!(?:'
18741       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18742       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18743       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18744     
18745     block.html = replace(block.html)
18746       ('comment', /<!--[\s\S]*?-->/)
18747       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18748       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18749       (/tag/g, block._tag)
18750       ();
18751     
18752     block.paragraph = replace(block.paragraph)
18753       ('hr', block.hr)
18754       ('heading', block.heading)
18755       ('lheading', block.lheading)
18756       ('blockquote', block.blockquote)
18757       ('tag', '<' + block._tag)
18758       ('def', block.def)
18759       ();
18760     
18761     /**
18762      * Normal Block Grammar
18763      */
18764     
18765     block.normal = merge({}, block);
18766     
18767     /**
18768      * GFM Block Grammar
18769      */
18770     
18771     block.gfm = merge({}, block.normal, {
18772       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18773       paragraph: /^/,
18774       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18775     });
18776     
18777     block.gfm.paragraph = replace(block.paragraph)
18778       ('(?!', '(?!'
18779         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18780         + block.list.source.replace('\\1', '\\3') + '|')
18781       ();
18782     
18783     /**
18784      * GFM + Tables Block Grammar
18785      */
18786     
18787     block.tables = merge({}, block.gfm, {
18788       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18789       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18790     });
18791     
18792     /**
18793      * Block Lexer
18794      */
18795     
18796     var Lexer = function (options) {
18797       this.tokens = [];
18798       this.tokens.links = {};
18799       this.options = options || marked.defaults;
18800       this.rules = block.normal;
18801     
18802       if (this.options.gfm) {
18803         if (this.options.tables) {
18804           this.rules = block.tables;
18805         } else {
18806           this.rules = block.gfm;
18807         }
18808       }
18809     }
18810     
18811     /**
18812      * Expose Block Rules
18813      */
18814     
18815     Lexer.rules = block;
18816     
18817     /**
18818      * Static Lex Method
18819      */
18820     
18821     Lexer.lex = function(src, options) {
18822       var lexer = new Lexer(options);
18823       return lexer.lex(src);
18824     };
18825     
18826     /**
18827      * Preprocessing
18828      */
18829     
18830     Lexer.prototype.lex = function(src) {
18831       src = src
18832         .replace(/\r\n|\r/g, '\n')
18833         .replace(/\t/g, '    ')
18834         .replace(/\u00a0/g, ' ')
18835         .replace(/\u2424/g, '\n');
18836     
18837       return this.token(src, true);
18838     };
18839     
18840     /**
18841      * Lexing
18842      */
18843     
18844     Lexer.prototype.token = function(src, top, bq) {
18845       var src = src.replace(/^ +$/gm, '')
18846         , next
18847         , loose
18848         , cap
18849         , bull
18850         , b
18851         , item
18852         , space
18853         , i
18854         , l;
18855     
18856       while (src) {
18857         // newline
18858         if (cap = this.rules.newline.exec(src)) {
18859           src = src.substring(cap[0].length);
18860           if (cap[0].length > 1) {
18861             this.tokens.push({
18862               type: 'space'
18863             });
18864           }
18865         }
18866     
18867         // code
18868         if (cap = this.rules.code.exec(src)) {
18869           src = src.substring(cap[0].length);
18870           cap = cap[0].replace(/^ {4}/gm, '');
18871           this.tokens.push({
18872             type: 'code',
18873             text: !this.options.pedantic
18874               ? cap.replace(/\n+$/, '')
18875               : cap
18876           });
18877           continue;
18878         }
18879     
18880         // fences (gfm)
18881         if (cap = this.rules.fences.exec(src)) {
18882           src = src.substring(cap[0].length);
18883           this.tokens.push({
18884             type: 'code',
18885             lang: cap[2],
18886             text: cap[3] || ''
18887           });
18888           continue;
18889         }
18890     
18891         // heading
18892         if (cap = this.rules.heading.exec(src)) {
18893           src = src.substring(cap[0].length);
18894           this.tokens.push({
18895             type: 'heading',
18896             depth: cap[1].length,
18897             text: cap[2]
18898           });
18899           continue;
18900         }
18901     
18902         // table no leading pipe (gfm)
18903         if (top && (cap = this.rules.nptable.exec(src))) {
18904           src = src.substring(cap[0].length);
18905     
18906           item = {
18907             type: 'table',
18908             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18909             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18910             cells: cap[3].replace(/\n$/, '').split('\n')
18911           };
18912     
18913           for (i = 0; i < item.align.length; i++) {
18914             if (/^ *-+: *$/.test(item.align[i])) {
18915               item.align[i] = 'right';
18916             } else if (/^ *:-+: *$/.test(item.align[i])) {
18917               item.align[i] = 'center';
18918             } else if (/^ *:-+ *$/.test(item.align[i])) {
18919               item.align[i] = 'left';
18920             } else {
18921               item.align[i] = null;
18922             }
18923           }
18924     
18925           for (i = 0; i < item.cells.length; i++) {
18926             item.cells[i] = item.cells[i].split(/ *\| */);
18927           }
18928     
18929           this.tokens.push(item);
18930     
18931           continue;
18932         }
18933     
18934         // lheading
18935         if (cap = this.rules.lheading.exec(src)) {
18936           src = src.substring(cap[0].length);
18937           this.tokens.push({
18938             type: 'heading',
18939             depth: cap[2] === '=' ? 1 : 2,
18940             text: cap[1]
18941           });
18942           continue;
18943         }
18944     
18945         // hr
18946         if (cap = this.rules.hr.exec(src)) {
18947           src = src.substring(cap[0].length);
18948           this.tokens.push({
18949             type: 'hr'
18950           });
18951           continue;
18952         }
18953     
18954         // blockquote
18955         if (cap = this.rules.blockquote.exec(src)) {
18956           src = src.substring(cap[0].length);
18957     
18958           this.tokens.push({
18959             type: 'blockquote_start'
18960           });
18961     
18962           cap = cap[0].replace(/^ *> ?/gm, '');
18963     
18964           // Pass `top` to keep the current
18965           // "toplevel" state. This is exactly
18966           // how markdown.pl works.
18967           this.token(cap, top, true);
18968     
18969           this.tokens.push({
18970             type: 'blockquote_end'
18971           });
18972     
18973           continue;
18974         }
18975     
18976         // list
18977         if (cap = this.rules.list.exec(src)) {
18978           src = src.substring(cap[0].length);
18979           bull = cap[2];
18980     
18981           this.tokens.push({
18982             type: 'list_start',
18983             ordered: bull.length > 1
18984           });
18985     
18986           // Get each top-level item.
18987           cap = cap[0].match(this.rules.item);
18988     
18989           next = false;
18990           l = cap.length;
18991           i = 0;
18992     
18993           for (; i < l; i++) {
18994             item = cap[i];
18995     
18996             // Remove the list item's bullet
18997             // so it is seen as the next token.
18998             space = item.length;
18999             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
19000     
19001             // Outdent whatever the
19002             // list item contains. Hacky.
19003             if (~item.indexOf('\n ')) {
19004               space -= item.length;
19005               item = !this.options.pedantic
19006                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
19007                 : item.replace(/^ {1,4}/gm, '');
19008             }
19009     
19010             // Determine whether the next list item belongs here.
19011             // Backpedal if it does not belong in this list.
19012             if (this.options.smartLists && i !== l - 1) {
19013               b = block.bullet.exec(cap[i + 1])[0];
19014               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
19015                 src = cap.slice(i + 1).join('\n') + src;
19016                 i = l - 1;
19017               }
19018             }
19019     
19020             // Determine whether item is loose or not.
19021             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
19022             // for discount behavior.
19023             loose = next || /\n\n(?!\s*$)/.test(item);
19024             if (i !== l - 1) {
19025               next = item.charAt(item.length - 1) === '\n';
19026               if (!loose) { loose = next; }
19027             }
19028     
19029             this.tokens.push({
19030               type: loose
19031                 ? 'loose_item_start'
19032                 : 'list_item_start'
19033             });
19034     
19035             // Recurse.
19036             this.token(item, false, bq);
19037     
19038             this.tokens.push({
19039               type: 'list_item_end'
19040             });
19041           }
19042     
19043           this.tokens.push({
19044             type: 'list_end'
19045           });
19046     
19047           continue;
19048         }
19049     
19050         // html
19051         if (cap = this.rules.html.exec(src)) {
19052           src = src.substring(cap[0].length);
19053           this.tokens.push({
19054             type: this.options.sanitize
19055               ? 'paragraph'
19056               : 'html',
19057             pre: !this.options.sanitizer
19058               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
19059             text: cap[0]
19060           });
19061           continue;
19062         }
19063     
19064         // def
19065         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
19066           src = src.substring(cap[0].length);
19067           this.tokens.links[cap[1].toLowerCase()] = {
19068             href: cap[2],
19069             title: cap[3]
19070           };
19071           continue;
19072         }
19073     
19074         // table (gfm)
19075         if (top && (cap = this.rules.table.exec(src))) {
19076           src = src.substring(cap[0].length);
19077     
19078           item = {
19079             type: 'table',
19080             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19081             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19082             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19083           };
19084     
19085           for (i = 0; i < item.align.length; i++) {
19086             if (/^ *-+: *$/.test(item.align[i])) {
19087               item.align[i] = 'right';
19088             } else if (/^ *:-+: *$/.test(item.align[i])) {
19089               item.align[i] = 'center';
19090             } else if (/^ *:-+ *$/.test(item.align[i])) {
19091               item.align[i] = 'left';
19092             } else {
19093               item.align[i] = null;
19094             }
19095           }
19096     
19097           for (i = 0; i < item.cells.length; i++) {
19098             item.cells[i] = item.cells[i]
19099               .replace(/^ *\| *| *\| *$/g, '')
19100               .split(/ *\| */);
19101           }
19102     
19103           this.tokens.push(item);
19104     
19105           continue;
19106         }
19107     
19108         // top-level paragraph
19109         if (top && (cap = this.rules.paragraph.exec(src))) {
19110           src = src.substring(cap[0].length);
19111           this.tokens.push({
19112             type: 'paragraph',
19113             text: cap[1].charAt(cap[1].length - 1) === '\n'
19114               ? cap[1].slice(0, -1)
19115               : cap[1]
19116           });
19117           continue;
19118         }
19119     
19120         // text
19121         if (cap = this.rules.text.exec(src)) {
19122           // Top-level should never reach here.
19123           src = src.substring(cap[0].length);
19124           this.tokens.push({
19125             type: 'text',
19126             text: cap[0]
19127           });
19128           continue;
19129         }
19130     
19131         if (src) {
19132           throw new
19133             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19134         }
19135       }
19136     
19137       return this.tokens;
19138     };
19139     
19140     /**
19141      * Inline-Level Grammar
19142      */
19143     
19144     var inline = {
19145       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19146       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19147       url: noop,
19148       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19149       link: /^!?\[(inside)\]\(href\)/,
19150       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19151       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19152       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19153       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19154       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19155       br: /^ {2,}\n(?!\s*$)/,
19156       del: noop,
19157       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19158     };
19159     
19160     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
19161     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19162     
19163     inline.link = replace(inline.link)
19164       ('inside', inline._inside)
19165       ('href', inline._href)
19166       ();
19167     
19168     inline.reflink = replace(inline.reflink)
19169       ('inside', inline._inside)
19170       ();
19171     
19172     /**
19173      * Normal Inline Grammar
19174      */
19175     
19176     inline.normal = merge({}, inline);
19177     
19178     /**
19179      * Pedantic Inline Grammar
19180      */
19181     
19182     inline.pedantic = merge({}, inline.normal, {
19183       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19184       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19185     });
19186     
19187     /**
19188      * GFM Inline Grammar
19189      */
19190     
19191     inline.gfm = merge({}, inline.normal, {
19192       escape: replace(inline.escape)('])', '~|])')(),
19193       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19194       del: /^~~(?=\S)([\s\S]*?\S)~~/,
19195       text: replace(inline.text)
19196         (']|', '~]|')
19197         ('|', '|https?://|')
19198         ()
19199     });
19200     
19201     /**
19202      * GFM + Line Breaks Inline Grammar
19203      */
19204     
19205     inline.breaks = merge({}, inline.gfm, {
19206       br: replace(inline.br)('{2,}', '*')(),
19207       text: replace(inline.gfm.text)('{2,}', '*')()
19208     });
19209     
19210     /**
19211      * Inline Lexer & Compiler
19212      */
19213     
19214     var InlineLexer  = function (links, options) {
19215       this.options = options || marked.defaults;
19216       this.links = links;
19217       this.rules = inline.normal;
19218       this.renderer = this.options.renderer || new Renderer;
19219       this.renderer.options = this.options;
19220     
19221       if (!this.links) {
19222         throw new
19223           Error('Tokens array requires a `links` property.');
19224       }
19225     
19226       if (this.options.gfm) {
19227         if (this.options.breaks) {
19228           this.rules = inline.breaks;
19229         } else {
19230           this.rules = inline.gfm;
19231         }
19232       } else if (this.options.pedantic) {
19233         this.rules = inline.pedantic;
19234       }
19235     }
19236     
19237     /**
19238      * Expose Inline Rules
19239      */
19240     
19241     InlineLexer.rules = inline;
19242     
19243     /**
19244      * Static Lexing/Compiling Method
19245      */
19246     
19247     InlineLexer.output = function(src, links, options) {
19248       var inline = new InlineLexer(links, options);
19249       return inline.output(src);
19250     };
19251     
19252     /**
19253      * Lexing/Compiling
19254      */
19255     
19256     InlineLexer.prototype.output = function(src) {
19257       var out = ''
19258         , link
19259         , text
19260         , href
19261         , cap;
19262     
19263       while (src) {
19264         // escape
19265         if (cap = this.rules.escape.exec(src)) {
19266           src = src.substring(cap[0].length);
19267           out += cap[1];
19268           continue;
19269         }
19270     
19271         // autolink
19272         if (cap = this.rules.autolink.exec(src)) {
19273           src = src.substring(cap[0].length);
19274           if (cap[2] === '@') {
19275             text = cap[1].charAt(6) === ':'
19276               ? this.mangle(cap[1].substring(7))
19277               : this.mangle(cap[1]);
19278             href = this.mangle('mailto:') + text;
19279           } else {
19280             text = escape(cap[1]);
19281             href = text;
19282           }
19283           out += this.renderer.link(href, null, text);
19284           continue;
19285         }
19286     
19287         // url (gfm)
19288         if (!this.inLink && (cap = this.rules.url.exec(src))) {
19289           src = src.substring(cap[0].length);
19290           text = escape(cap[1]);
19291           href = text;
19292           out += this.renderer.link(href, null, text);
19293           continue;
19294         }
19295     
19296         // tag
19297         if (cap = this.rules.tag.exec(src)) {
19298           if (!this.inLink && /^<a /i.test(cap[0])) {
19299             this.inLink = true;
19300           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19301             this.inLink = false;
19302           }
19303           src = src.substring(cap[0].length);
19304           out += this.options.sanitize
19305             ? this.options.sanitizer
19306               ? this.options.sanitizer(cap[0])
19307               : escape(cap[0])
19308             : cap[0];
19309           continue;
19310         }
19311     
19312         // link
19313         if (cap = this.rules.link.exec(src)) {
19314           src = src.substring(cap[0].length);
19315           this.inLink = true;
19316           out += this.outputLink(cap, {
19317             href: cap[2],
19318             title: cap[3]
19319           });
19320           this.inLink = false;
19321           continue;
19322         }
19323     
19324         // reflink, nolink
19325         if ((cap = this.rules.reflink.exec(src))
19326             || (cap = this.rules.nolink.exec(src))) {
19327           src = src.substring(cap[0].length);
19328           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19329           link = this.links[link.toLowerCase()];
19330           if (!link || !link.href) {
19331             out += cap[0].charAt(0);
19332             src = cap[0].substring(1) + src;
19333             continue;
19334           }
19335           this.inLink = true;
19336           out += this.outputLink(cap, link);
19337           this.inLink = false;
19338           continue;
19339         }
19340     
19341         // strong
19342         if (cap = this.rules.strong.exec(src)) {
19343           src = src.substring(cap[0].length);
19344           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19345           continue;
19346         }
19347     
19348         // em
19349         if (cap = this.rules.em.exec(src)) {
19350           src = src.substring(cap[0].length);
19351           out += this.renderer.em(this.output(cap[2] || cap[1]));
19352           continue;
19353         }
19354     
19355         // code
19356         if (cap = this.rules.code.exec(src)) {
19357           src = src.substring(cap[0].length);
19358           out += this.renderer.codespan(escape(cap[2], true));
19359           continue;
19360         }
19361     
19362         // br
19363         if (cap = this.rules.br.exec(src)) {
19364           src = src.substring(cap[0].length);
19365           out += this.renderer.br();
19366           continue;
19367         }
19368     
19369         // del (gfm)
19370         if (cap = this.rules.del.exec(src)) {
19371           src = src.substring(cap[0].length);
19372           out += this.renderer.del(this.output(cap[1]));
19373           continue;
19374         }
19375     
19376         // text
19377         if (cap = this.rules.text.exec(src)) {
19378           src = src.substring(cap[0].length);
19379           out += this.renderer.text(escape(this.smartypants(cap[0])));
19380           continue;
19381         }
19382     
19383         if (src) {
19384           throw new
19385             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19386         }
19387       }
19388     
19389       return out;
19390     };
19391     
19392     /**
19393      * Compile Link
19394      */
19395     
19396     InlineLexer.prototype.outputLink = function(cap, link) {
19397       var href = escape(link.href)
19398         , title = link.title ? escape(link.title) : null;
19399     
19400       return cap[0].charAt(0) !== '!'
19401         ? this.renderer.link(href, title, this.output(cap[1]))
19402         : this.renderer.image(href, title, escape(cap[1]));
19403     };
19404     
19405     /**
19406      * Smartypants Transformations
19407      */
19408     
19409     InlineLexer.prototype.smartypants = function(text) {
19410       if (!this.options.smartypants)  { return text; }
19411       return text
19412         // em-dashes
19413         .replace(/---/g, '\u2014')
19414         // en-dashes
19415         .replace(/--/g, '\u2013')
19416         // opening singles
19417         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19418         // closing singles & apostrophes
19419         .replace(/'/g, '\u2019')
19420         // opening doubles
19421         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19422         // closing doubles
19423         .replace(/"/g, '\u201d')
19424         // ellipses
19425         .replace(/\.{3}/g, '\u2026');
19426     };
19427     
19428     /**
19429      * Mangle Links
19430      */
19431     
19432     InlineLexer.prototype.mangle = function(text) {
19433       if (!this.options.mangle) { return text; }
19434       var out = ''
19435         , l = text.length
19436         , i = 0
19437         , ch;
19438     
19439       for (; i < l; i++) {
19440         ch = text.charCodeAt(i);
19441         if (Math.random() > 0.5) {
19442           ch = 'x' + ch.toString(16);
19443         }
19444         out += '&#' + ch + ';';
19445       }
19446     
19447       return out;
19448     };
19449     
19450     /**
19451      * Renderer
19452      */
19453     
19454      /**
19455          * eval:var:Renderer
19456     */
19457     
19458     var Renderer   = function (options) {
19459       this.options = options || {};
19460     }
19461     
19462     Renderer.prototype.code = function(code, lang, escaped) {
19463       if (this.options.highlight) {
19464         var out = this.options.highlight(code, lang);
19465         if (out != null && out !== code) {
19466           escaped = true;
19467           code = out;
19468         }
19469       } else {
19470             // hack!!! - it's already escapeD?
19471             escaped = true;
19472       }
19473     
19474       if (!lang) {
19475         return '<pre><code>'
19476           + (escaped ? code : escape(code, true))
19477           + '\n</code></pre>';
19478       }
19479     
19480       return '<pre><code class="'
19481         + this.options.langPrefix
19482         + escape(lang, true)
19483         + '">'
19484         + (escaped ? code : escape(code, true))
19485         + '\n</code></pre>\n';
19486     };
19487     
19488     Renderer.prototype.blockquote = function(quote) {
19489       return '<blockquote>\n' + quote + '</blockquote>\n';
19490     };
19491     
19492     Renderer.prototype.html = function(html) {
19493       return html;
19494     };
19495     
19496     Renderer.prototype.heading = function(text, level, raw) {
19497       return '<h'
19498         + level
19499         + ' id="'
19500         + this.options.headerPrefix
19501         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19502         + '">'
19503         + text
19504         + '</h'
19505         + level
19506         + '>\n';
19507     };
19508     
19509     Renderer.prototype.hr = function() {
19510       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19511     };
19512     
19513     Renderer.prototype.list = function(body, ordered) {
19514       var type = ordered ? 'ol' : 'ul';
19515       return '<' + type + '>\n' + body + '</' + type + '>\n';
19516     };
19517     
19518     Renderer.prototype.listitem = function(text) {
19519       return '<li>' + text + '</li>\n';
19520     };
19521     
19522     Renderer.prototype.paragraph = function(text) {
19523       return '<p>' + text + '</p>\n';
19524     };
19525     
19526     Renderer.prototype.table = function(header, body) {
19527       return '<table class="table table-striped">\n'
19528         + '<thead>\n'
19529         + header
19530         + '</thead>\n'
19531         + '<tbody>\n'
19532         + body
19533         + '</tbody>\n'
19534         + '</table>\n';
19535     };
19536     
19537     Renderer.prototype.tablerow = function(content) {
19538       return '<tr>\n' + content + '</tr>\n';
19539     };
19540     
19541     Renderer.prototype.tablecell = function(content, flags) {
19542       var type = flags.header ? 'th' : 'td';
19543       var tag = flags.align
19544         ? '<' + type + ' style="text-align:' + flags.align + '">'
19545         : '<' + type + '>';
19546       return tag + content + '</' + type + '>\n';
19547     };
19548     
19549     // span level renderer
19550     Renderer.prototype.strong = function(text) {
19551       return '<strong>' + text + '</strong>';
19552     };
19553     
19554     Renderer.prototype.em = function(text) {
19555       return '<em>' + text + '</em>';
19556     };
19557     
19558     Renderer.prototype.codespan = function(text) {
19559       return '<code>' + text + '</code>';
19560     };
19561     
19562     Renderer.prototype.br = function() {
19563       return this.options.xhtml ? '<br/>' : '<br>';
19564     };
19565     
19566     Renderer.prototype.del = function(text) {
19567       return '<del>' + text + '</del>';
19568     };
19569     
19570     Renderer.prototype.link = function(href, title, text) {
19571       if (this.options.sanitize) {
19572         try {
19573           var prot = decodeURIComponent(unescape(href))
19574             .replace(/[^\w:]/g, '')
19575             .toLowerCase();
19576         } catch (e) {
19577           return '';
19578         }
19579         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19580           return '';
19581         }
19582       }
19583       var out = '<a href="' + href + '"';
19584       if (title) {
19585         out += ' title="' + title + '"';
19586       }
19587       out += '>' + text + '</a>';
19588       return out;
19589     };
19590     
19591     Renderer.prototype.image = function(href, title, text) {
19592       var out = '<img src="' + href + '" alt="' + text + '"';
19593       if (title) {
19594         out += ' title="' + title + '"';
19595       }
19596       out += this.options.xhtml ? '/>' : '>';
19597       return out;
19598     };
19599     
19600     Renderer.prototype.text = function(text) {
19601       return text;
19602     };
19603     
19604     /**
19605      * Parsing & Compiling
19606      */
19607          /**
19608          * eval:var:Parser
19609     */
19610     
19611     var Parser= function (options) {
19612       this.tokens = [];
19613       this.token = null;
19614       this.options = options || marked.defaults;
19615       this.options.renderer = this.options.renderer || new Renderer;
19616       this.renderer = this.options.renderer;
19617       this.renderer.options = this.options;
19618     }
19619     
19620     /**
19621      * Static Parse Method
19622      */
19623     
19624     Parser.parse = function(src, options, renderer) {
19625       var parser = new Parser(options, renderer);
19626       return parser.parse(src);
19627     };
19628     
19629     /**
19630      * Parse Loop
19631      */
19632     
19633     Parser.prototype.parse = function(src) {
19634       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19635       this.tokens = src.reverse();
19636     
19637       var out = '';
19638       while (this.next()) {
19639         out += this.tok();
19640       }
19641     
19642       return out;
19643     };
19644     
19645     /**
19646      * Next Token
19647      */
19648     
19649     Parser.prototype.next = function() {
19650       return this.token = this.tokens.pop();
19651     };
19652     
19653     /**
19654      * Preview Next Token
19655      */
19656     
19657     Parser.prototype.peek = function() {
19658       return this.tokens[this.tokens.length - 1] || 0;
19659     };
19660     
19661     /**
19662      * Parse Text Tokens
19663      */
19664     
19665     Parser.prototype.parseText = function() {
19666       var body = this.token.text;
19667     
19668       while (this.peek().type === 'text') {
19669         body += '\n' + this.next().text;
19670       }
19671     
19672       return this.inline.output(body);
19673     };
19674     
19675     /**
19676      * Parse Current Token
19677      */
19678     
19679     Parser.prototype.tok = function() {
19680       switch (this.token.type) {
19681         case 'space': {
19682           return '';
19683         }
19684         case 'hr': {
19685           return this.renderer.hr();
19686         }
19687         case 'heading': {
19688           return this.renderer.heading(
19689             this.inline.output(this.token.text),
19690             this.token.depth,
19691             this.token.text);
19692         }
19693         case 'code': {
19694           return this.renderer.code(this.token.text,
19695             this.token.lang,
19696             this.token.escaped);
19697         }
19698         case 'table': {
19699           var header = ''
19700             , body = ''
19701             , i
19702             , row
19703             , cell
19704             , flags
19705             , j;
19706     
19707           // header
19708           cell = '';
19709           for (i = 0; i < this.token.header.length; i++) {
19710             flags = { header: true, align: this.token.align[i] };
19711             cell += this.renderer.tablecell(
19712               this.inline.output(this.token.header[i]),
19713               { header: true, align: this.token.align[i] }
19714             );
19715           }
19716           header += this.renderer.tablerow(cell);
19717     
19718           for (i = 0; i < this.token.cells.length; i++) {
19719             row = this.token.cells[i];
19720     
19721             cell = '';
19722             for (j = 0; j < row.length; j++) {
19723               cell += this.renderer.tablecell(
19724                 this.inline.output(row[j]),
19725                 { header: false, align: this.token.align[j] }
19726               );
19727             }
19728     
19729             body += this.renderer.tablerow(cell);
19730           }
19731           return this.renderer.table(header, body);
19732         }
19733         case 'blockquote_start': {
19734           var body = '';
19735     
19736           while (this.next().type !== 'blockquote_end') {
19737             body += this.tok();
19738           }
19739     
19740           return this.renderer.blockquote(body);
19741         }
19742         case 'list_start': {
19743           var body = ''
19744             , ordered = this.token.ordered;
19745     
19746           while (this.next().type !== 'list_end') {
19747             body += this.tok();
19748           }
19749     
19750           return this.renderer.list(body, ordered);
19751         }
19752         case 'list_item_start': {
19753           var body = '';
19754     
19755           while (this.next().type !== 'list_item_end') {
19756             body += this.token.type === 'text'
19757               ? this.parseText()
19758               : this.tok();
19759           }
19760     
19761           return this.renderer.listitem(body);
19762         }
19763         case 'loose_item_start': {
19764           var body = '';
19765     
19766           while (this.next().type !== 'list_item_end') {
19767             body += this.tok();
19768           }
19769     
19770           return this.renderer.listitem(body);
19771         }
19772         case 'html': {
19773           var html = !this.token.pre && !this.options.pedantic
19774             ? this.inline.output(this.token.text)
19775             : this.token.text;
19776           return this.renderer.html(html);
19777         }
19778         case 'paragraph': {
19779           return this.renderer.paragraph(this.inline.output(this.token.text));
19780         }
19781         case 'text': {
19782           return this.renderer.paragraph(this.parseText());
19783         }
19784       }
19785     };
19786   
19787     
19788     /**
19789      * Marked
19790      */
19791          /**
19792          * eval:var:marked
19793     */
19794     var marked = function (src, opt, callback) {
19795       if (callback || typeof opt === 'function') {
19796         if (!callback) {
19797           callback = opt;
19798           opt = null;
19799         }
19800     
19801         opt = merge({}, marked.defaults, opt || {});
19802     
19803         var highlight = opt.highlight
19804           , tokens
19805           , pending
19806           , i = 0;
19807     
19808         try {
19809           tokens = Lexer.lex(src, opt)
19810         } catch (e) {
19811           return callback(e);
19812         }
19813     
19814         pending = tokens.length;
19815          /**
19816          * eval:var:done
19817     */
19818         var done = function(err) {
19819           if (err) {
19820             opt.highlight = highlight;
19821             return callback(err);
19822           }
19823     
19824           var out;
19825     
19826           try {
19827             out = Parser.parse(tokens, opt);
19828           } catch (e) {
19829             err = e;
19830           }
19831     
19832           opt.highlight = highlight;
19833     
19834           return err
19835             ? callback(err)
19836             : callback(null, out);
19837         };
19838     
19839         if (!highlight || highlight.length < 3) {
19840           return done();
19841         }
19842     
19843         delete opt.highlight;
19844     
19845         if (!pending) { return done(); }
19846     
19847         for (; i < tokens.length; i++) {
19848           (function(token) {
19849             if (token.type !== 'code') {
19850               return --pending || done();
19851             }
19852             return highlight(token.text, token.lang, function(err, code) {
19853               if (err) { return done(err); }
19854               if (code == null || code === token.text) {
19855                 return --pending || done();
19856               }
19857               token.text = code;
19858               token.escaped = true;
19859               --pending || done();
19860             });
19861           })(tokens[i]);
19862         }
19863     
19864         return;
19865       }
19866       try {
19867         if (opt) { opt = merge({}, marked.defaults, opt); }
19868         return Parser.parse(Lexer.lex(src, opt), opt);
19869       } catch (e) {
19870         e.message += '\nPlease report this to https://github.com/chjj/marked.';
19871         if ((opt || marked.defaults).silent) {
19872           return '<p>An error occured:</p><pre>'
19873             + escape(e.message + '', true)
19874             + '</pre>';
19875         }
19876         throw e;
19877       }
19878     }
19879     
19880     /**
19881      * Options
19882      */
19883     
19884     marked.options =
19885     marked.setOptions = function(opt) {
19886       merge(marked.defaults, opt);
19887       return marked;
19888     };
19889     
19890     marked.defaults = {
19891       gfm: true,
19892       tables: true,
19893       breaks: false,
19894       pedantic: false,
19895       sanitize: false,
19896       sanitizer: null,
19897       mangle: true,
19898       smartLists: false,
19899       silent: false,
19900       highlight: null,
19901       langPrefix: 'lang-',
19902       smartypants: false,
19903       headerPrefix: '',
19904       renderer: new Renderer,
19905       xhtml: false
19906     };
19907     
19908     /**
19909      * Expose
19910      */
19911     
19912     marked.Parser = Parser;
19913     marked.parser = Parser.parse;
19914     
19915     marked.Renderer = Renderer;
19916     
19917     marked.Lexer = Lexer;
19918     marked.lexer = Lexer.lex;
19919     
19920     marked.InlineLexer = InlineLexer;
19921     marked.inlineLexer = InlineLexer.output;
19922     
19923     marked.parse = marked;
19924     
19925     Roo.Markdown.marked = marked;
19926
19927 })();/*
19928  * Based on:
19929  * Ext JS Library 1.1.1
19930  * Copyright(c) 2006-2007, Ext JS, LLC.
19931  *
19932  * Originally Released Under LGPL - original licence link has changed is not relivant.
19933  *
19934  * Fork - LGPL
19935  * <script type="text/javascript">
19936  */
19937
19938
19939
19940 /*
19941  * These classes are derivatives of the similarly named classes in the YUI Library.
19942  * The original license:
19943  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19944  * Code licensed under the BSD License:
19945  * http://developer.yahoo.net/yui/license.txt
19946  */
19947
19948 (function() {
19949
19950 var Event=Roo.EventManager;
19951 var Dom=Roo.lib.Dom;
19952
19953 /**
19954  * @class Roo.dd.DragDrop
19955  * @extends Roo.util.Observable
19956  * Defines the interface and base operation of items that that can be
19957  * dragged or can be drop targets.  It was designed to be extended, overriding
19958  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19959  * Up to three html elements can be associated with a DragDrop instance:
19960  * <ul>
19961  * <li>linked element: the element that is passed into the constructor.
19962  * This is the element which defines the boundaries for interaction with
19963  * other DragDrop objects.</li>
19964  * <li>handle element(s): The drag operation only occurs if the element that
19965  * was clicked matches a handle element.  By default this is the linked
19966  * element, but there are times that you will want only a portion of the
19967  * linked element to initiate the drag operation, and the setHandleElId()
19968  * method provides a way to define this.</li>
19969  * <li>drag element: this represents the element that would be moved along
19970  * with the cursor during a drag operation.  By default, this is the linked
19971  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
19972  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19973  * </li>
19974  * </ul>
19975  * This class should not be instantiated until the onload event to ensure that
19976  * the associated elements are available.
19977  * The following would define a DragDrop obj that would interact with any
19978  * other DragDrop obj in the "group1" group:
19979  * <pre>
19980  *  dd = new Roo.dd.DragDrop("div1", "group1");
19981  * </pre>
19982  * Since none of the event handlers have been implemented, nothing would
19983  * actually happen if you were to run the code above.  Normally you would
19984  * override this class or one of the default implementations, but you can
19985  * also override the methods you want on an instance of the class...
19986  * <pre>
19987  *  dd.onDragDrop = function(e, id) {
19988  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
19989  *  }
19990  * </pre>
19991  * @constructor
19992  * @param {String} id of the element that is linked to this instance
19993  * @param {String} sGroup the group of related DragDrop objects
19994  * @param {object} config an object containing configurable attributes
19995  *                Valid properties for DragDrop:
19996  *                    padding, isTarget, maintainOffset, primaryButtonOnly
19997  */
19998 Roo.dd.DragDrop = function(id, sGroup, config) {
19999     if (id) {
20000         this.init(id, sGroup, config);
20001     }
20002     
20003 };
20004
20005 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
20006
20007     /**
20008      * The id of the element associated with this object.  This is what we
20009      * refer to as the "linked element" because the size and position of
20010      * this element is used to determine when the drag and drop objects have
20011      * interacted.
20012      * @property id
20013      * @type String
20014      */
20015     id: null,
20016
20017     /**
20018      * Configuration attributes passed into the constructor
20019      * @property config
20020      * @type object
20021      */
20022     config: null,
20023
20024     /**
20025      * The id of the element that will be dragged.  By default this is same
20026      * as the linked element , but could be changed to another element. Ex:
20027      * Roo.dd.DDProxy
20028      * @property dragElId
20029      * @type String
20030      * @private
20031      */
20032     dragElId: null,
20033
20034     /**
20035      * the id of the element that initiates the drag operation.  By default
20036      * this is the linked element, but could be changed to be a child of this
20037      * element.  This lets us do things like only starting the drag when the
20038      * header element within the linked html element is clicked.
20039      * @property handleElId
20040      * @type String
20041      * @private
20042      */
20043     handleElId: null,
20044
20045     /**
20046      * An associative array of HTML tags that will be ignored if clicked.
20047      * @property invalidHandleTypes
20048      * @type {string: string}
20049      */
20050     invalidHandleTypes: null,
20051
20052     /**
20053      * An associative array of ids for elements that will be ignored if clicked
20054      * @property invalidHandleIds
20055      * @type {string: string}
20056      */
20057     invalidHandleIds: null,
20058
20059     /**
20060      * An indexted array of css class names for elements that will be ignored
20061      * if clicked.
20062      * @property invalidHandleClasses
20063      * @type string[]
20064      */
20065     invalidHandleClasses: null,
20066
20067     /**
20068      * The linked element's absolute X position at the time the drag was
20069      * started
20070      * @property startPageX
20071      * @type int
20072      * @private
20073      */
20074     startPageX: 0,
20075
20076     /**
20077      * The linked element's absolute X position at the time the drag was
20078      * started
20079      * @property startPageY
20080      * @type int
20081      * @private
20082      */
20083     startPageY: 0,
20084
20085     /**
20086      * The group defines a logical collection of DragDrop objects that are
20087      * related.  Instances only get events when interacting with other
20088      * DragDrop object in the same group.  This lets us define multiple
20089      * groups using a single DragDrop subclass if we want.
20090      * @property groups
20091      * @type {string: string}
20092      */
20093     groups: null,
20094
20095     /**
20096      * Individual drag/drop instances can be locked.  This will prevent
20097      * onmousedown start drag.
20098      * @property locked
20099      * @type boolean
20100      * @private
20101      */
20102     locked: false,
20103
20104     /**
20105      * Lock this instance
20106      * @method lock
20107      */
20108     lock: function() { this.locked = true; },
20109
20110     /**
20111      * Unlock this instace
20112      * @method unlock
20113      */
20114     unlock: function() { this.locked = false; },
20115
20116     /**
20117      * By default, all insances can be a drop target.  This can be disabled by
20118      * setting isTarget to false.
20119      * @method isTarget
20120      * @type boolean
20121      */
20122     isTarget: true,
20123
20124     /**
20125      * The padding configured for this drag and drop object for calculating
20126      * the drop zone intersection with this object.
20127      * @method padding
20128      * @type int[]
20129      */
20130     padding: null,
20131
20132     /**
20133      * Cached reference to the linked element
20134      * @property _domRef
20135      * @private
20136      */
20137     _domRef: null,
20138
20139     /**
20140      * Internal typeof flag
20141      * @property __ygDragDrop
20142      * @private
20143      */
20144     __ygDragDrop: true,
20145
20146     /**
20147      * Set to true when horizontal contraints are applied
20148      * @property constrainX
20149      * @type boolean
20150      * @private
20151      */
20152     constrainX: false,
20153
20154     /**
20155      * Set to true when vertical contraints are applied
20156      * @property constrainY
20157      * @type boolean
20158      * @private
20159      */
20160     constrainY: false,
20161
20162     /**
20163      * The left constraint
20164      * @property minX
20165      * @type int
20166      * @private
20167      */
20168     minX: 0,
20169
20170     /**
20171      * The right constraint
20172      * @property maxX
20173      * @type int
20174      * @private
20175      */
20176     maxX: 0,
20177
20178     /**
20179      * The up constraint
20180      * @property minY
20181      * @type int
20182      * @type int
20183      * @private
20184      */
20185     minY: 0,
20186
20187     /**
20188      * The down constraint
20189      * @property maxY
20190      * @type int
20191      * @private
20192      */
20193     maxY: 0,
20194
20195     /**
20196      * Maintain offsets when we resetconstraints.  Set to true when you want
20197      * the position of the element relative to its parent to stay the same
20198      * when the page changes
20199      *
20200      * @property maintainOffset
20201      * @type boolean
20202      */
20203     maintainOffset: false,
20204
20205     /**
20206      * Array of pixel locations the element will snap to if we specified a
20207      * horizontal graduation/interval.  This array is generated automatically
20208      * when you define a tick interval.
20209      * @property xTicks
20210      * @type int[]
20211      */
20212     xTicks: null,
20213
20214     /**
20215      * Array of pixel locations the element will snap to if we specified a
20216      * vertical graduation/interval.  This array is generated automatically
20217      * when you define a tick interval.
20218      * @property yTicks
20219      * @type int[]
20220      */
20221     yTicks: null,
20222
20223     /**
20224      * By default the drag and drop instance will only respond to the primary
20225      * button click (left button for a right-handed mouse).  Set to true to
20226      * allow drag and drop to start with any mouse click that is propogated
20227      * by the browser
20228      * @property primaryButtonOnly
20229      * @type boolean
20230      */
20231     primaryButtonOnly: true,
20232
20233     /**
20234      * The availabe property is false until the linked dom element is accessible.
20235      * @property available
20236      * @type boolean
20237      */
20238     available: false,
20239
20240     /**
20241      * By default, drags can only be initiated if the mousedown occurs in the
20242      * region the linked element is.  This is done in part to work around a
20243      * bug in some browsers that mis-report the mousedown if the previous
20244      * mouseup happened outside of the window.  This property is set to true
20245      * if outer handles are defined.
20246      *
20247      * @property hasOuterHandles
20248      * @type boolean
20249      * @default false
20250      */
20251     hasOuterHandles: false,
20252
20253     /**
20254      * Code that executes immediately before the startDrag event
20255      * @method b4StartDrag
20256      * @private
20257      */
20258     b4StartDrag: function(x, y) { },
20259
20260     /**
20261      * Abstract method called after a drag/drop object is clicked
20262      * and the drag or mousedown time thresholds have beeen met.
20263      * @method startDrag
20264      * @param {int} X click location
20265      * @param {int} Y click location
20266      */
20267     startDrag: function(x, y) { /* override this */ },
20268
20269     /**
20270      * Code that executes immediately before the onDrag event
20271      * @method b4Drag
20272      * @private
20273      */
20274     b4Drag: function(e) { },
20275
20276     /**
20277      * Abstract method called during the onMouseMove event while dragging an
20278      * object.
20279      * @method onDrag
20280      * @param {Event} e the mousemove event
20281      */
20282     onDrag: function(e) { /* override this */ },
20283
20284     /**
20285      * Abstract method called when this element fist begins hovering over
20286      * another DragDrop obj
20287      * @method onDragEnter
20288      * @param {Event} e the mousemove event
20289      * @param {String|DragDrop[]} id In POINT mode, the element
20290      * id this is hovering over.  In INTERSECT mode, an array of one or more
20291      * dragdrop items being hovered over.
20292      */
20293     onDragEnter: function(e, id) { /* override this */ },
20294
20295     /**
20296      * Code that executes immediately before the onDragOver event
20297      * @method b4DragOver
20298      * @private
20299      */
20300     b4DragOver: function(e) { },
20301
20302     /**
20303      * Abstract method called when this element is hovering over another
20304      * DragDrop obj
20305      * @method onDragOver
20306      * @param {Event} e the mousemove event
20307      * @param {String|DragDrop[]} id In POINT mode, the element
20308      * id this is hovering over.  In INTERSECT mode, an array of dd items
20309      * being hovered over.
20310      */
20311     onDragOver: function(e, id) { /* override this */ },
20312
20313     /**
20314      * Code that executes immediately before the onDragOut event
20315      * @method b4DragOut
20316      * @private
20317      */
20318     b4DragOut: function(e) { },
20319
20320     /**
20321      * Abstract method called when we are no longer hovering over an element
20322      * @method onDragOut
20323      * @param {Event} e the mousemove event
20324      * @param {String|DragDrop[]} id In POINT mode, the element
20325      * id this was hovering over.  In INTERSECT mode, an array of dd items
20326      * that the mouse is no longer over.
20327      */
20328     onDragOut: function(e, id) { /* override this */ },
20329
20330     /**
20331      * Code that executes immediately before the onDragDrop event
20332      * @method b4DragDrop
20333      * @private
20334      */
20335     b4DragDrop: function(e) { },
20336
20337     /**
20338      * Abstract method called when this item is dropped on another DragDrop
20339      * obj
20340      * @method onDragDrop
20341      * @param {Event} e the mouseup event
20342      * @param {String|DragDrop[]} id In POINT mode, the element
20343      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20344      * was dropped on.
20345      */
20346     onDragDrop: function(e, id) { /* override this */ },
20347
20348     /**
20349      * Abstract method called when this item is dropped on an area with no
20350      * drop target
20351      * @method onInvalidDrop
20352      * @param {Event} e the mouseup event
20353      */
20354     onInvalidDrop: function(e) { /* override this */ },
20355
20356     /**
20357      * Code that executes immediately before the endDrag event
20358      * @method b4EndDrag
20359      * @private
20360      */
20361     b4EndDrag: function(e) { },
20362
20363     /**
20364      * Fired when we are done dragging the object
20365      * @method endDrag
20366      * @param {Event} e the mouseup event
20367      */
20368     endDrag: function(e) { /* override this */ },
20369
20370     /**
20371      * Code executed immediately before the onMouseDown event
20372      * @method b4MouseDown
20373      * @param {Event} e the mousedown event
20374      * @private
20375      */
20376     b4MouseDown: function(e) {  },
20377
20378     /**
20379      * Event handler that fires when a drag/drop obj gets a mousedown
20380      * @method onMouseDown
20381      * @param {Event} e the mousedown event
20382      */
20383     onMouseDown: function(e) { /* override this */ },
20384
20385     /**
20386      * Event handler that fires when a drag/drop obj gets a mouseup
20387      * @method onMouseUp
20388      * @param {Event} e the mouseup event
20389      */
20390     onMouseUp: function(e) { /* override this */ },
20391
20392     /**
20393      * Override the onAvailable method to do what is needed after the initial
20394      * position was determined.
20395      * @method onAvailable
20396      */
20397     onAvailable: function () {
20398     },
20399
20400     /*
20401      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20402      * @type Object
20403      */
20404     defaultPadding : {left:0, right:0, top:0, bottom:0},
20405
20406     /*
20407      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20408  *
20409  * Usage:
20410  <pre><code>
20411  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20412                 { dragElId: "existingProxyDiv" });
20413  dd.startDrag = function(){
20414      this.constrainTo("parent-id");
20415  };
20416  </code></pre>
20417  * Or you can initalize it using the {@link Roo.Element} object:
20418  <pre><code>
20419  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20420      startDrag : function(){
20421          this.constrainTo("parent-id");
20422      }
20423  });
20424  </code></pre>
20425      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20426      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20427      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20428      * an object containing the sides to pad. For example: {right:10, bottom:10}
20429      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20430      */
20431     constrainTo : function(constrainTo, pad, inContent){
20432         if(typeof pad == "number"){
20433             pad = {left: pad, right:pad, top:pad, bottom:pad};
20434         }
20435         pad = pad || this.defaultPadding;
20436         var b = Roo.get(this.getEl()).getBox();
20437         var ce = Roo.get(constrainTo);
20438         var s = ce.getScroll();
20439         var c, cd = ce.dom;
20440         if(cd == document.body){
20441             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20442         }else{
20443             xy = ce.getXY();
20444             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20445         }
20446
20447
20448         var topSpace = b.y - c.y;
20449         var leftSpace = b.x - c.x;
20450
20451         this.resetConstraints();
20452         this.setXConstraint(leftSpace - (pad.left||0), // left
20453                 c.width - leftSpace - b.width - (pad.right||0) //right
20454         );
20455         this.setYConstraint(topSpace - (pad.top||0), //top
20456                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20457         );
20458     },
20459
20460     /**
20461      * Returns a reference to the linked element
20462      * @method getEl
20463      * @return {HTMLElement} the html element
20464      */
20465     getEl: function() {
20466         if (!this._domRef) {
20467             this._domRef = Roo.getDom(this.id);
20468         }
20469
20470         return this._domRef;
20471     },
20472
20473     /**
20474      * Returns a reference to the actual element to drag.  By default this is
20475      * the same as the html element, but it can be assigned to another
20476      * element. An example of this can be found in Roo.dd.DDProxy
20477      * @method getDragEl
20478      * @return {HTMLElement} the html element
20479      */
20480     getDragEl: function() {
20481         return Roo.getDom(this.dragElId);
20482     },
20483
20484     /**
20485      * Sets up the DragDrop object.  Must be called in the constructor of any
20486      * Roo.dd.DragDrop subclass
20487      * @method init
20488      * @param id the id of the linked element
20489      * @param {String} sGroup the group of related items
20490      * @param {object} config configuration attributes
20491      */
20492     init: function(id, sGroup, config) {
20493         this.initTarget(id, sGroup, config);
20494         if (!Roo.isTouch) {
20495             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20496         }
20497         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20498         // Event.on(this.id, "selectstart", Event.preventDefault);
20499     },
20500
20501     /**
20502      * Initializes Targeting functionality only... the object does not
20503      * get a mousedown handler.
20504      * @method initTarget
20505      * @param id the id of the linked element
20506      * @param {String} sGroup the group of related items
20507      * @param {object} config configuration attributes
20508      */
20509     initTarget: function(id, sGroup, config) {
20510
20511         // configuration attributes
20512         this.config = config || {};
20513
20514         // create a local reference to the drag and drop manager
20515         this.DDM = Roo.dd.DDM;
20516         // initialize the groups array
20517         this.groups = {};
20518
20519         // assume that we have an element reference instead of an id if the
20520         // parameter is not a string
20521         if (typeof id !== "string") {
20522             id = Roo.id(id);
20523         }
20524
20525         // set the id
20526         this.id = id;
20527
20528         // add to an interaction group
20529         this.addToGroup((sGroup) ? sGroup : "default");
20530
20531         // We don't want to register this as the handle with the manager
20532         // so we just set the id rather than calling the setter.
20533         this.handleElId = id;
20534
20535         // the linked element is the element that gets dragged by default
20536         this.setDragElId(id);
20537
20538         // by default, clicked anchors will not start drag operations.
20539         this.invalidHandleTypes = { A: "A" };
20540         this.invalidHandleIds = {};
20541         this.invalidHandleClasses = [];
20542
20543         this.applyConfig();
20544
20545         this.handleOnAvailable();
20546     },
20547
20548     /**
20549      * Applies the configuration parameters that were passed into the constructor.
20550      * This is supposed to happen at each level through the inheritance chain.  So
20551      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20552      * DragDrop in order to get all of the parameters that are available in
20553      * each object.
20554      * @method applyConfig
20555      */
20556     applyConfig: function() {
20557
20558         // configurable properties:
20559         //    padding, isTarget, maintainOffset, primaryButtonOnly
20560         this.padding           = this.config.padding || [0, 0, 0, 0];
20561         this.isTarget          = (this.config.isTarget !== false);
20562         this.maintainOffset    = (this.config.maintainOffset);
20563         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20564
20565     },
20566
20567     /**
20568      * Executed when the linked element is available
20569      * @method handleOnAvailable
20570      * @private
20571      */
20572     handleOnAvailable: function() {
20573         this.available = true;
20574         this.resetConstraints();
20575         this.onAvailable();
20576     },
20577
20578      /**
20579      * Configures the padding for the target zone in px.  Effectively expands
20580      * (or reduces) the virtual object size for targeting calculations.
20581      * Supports css-style shorthand; if only one parameter is passed, all sides
20582      * will have that padding, and if only two are passed, the top and bottom
20583      * will have the first param, the left and right the second.
20584      * @method setPadding
20585      * @param {int} iTop    Top pad
20586      * @param {int} iRight  Right pad
20587      * @param {int} iBot    Bot pad
20588      * @param {int} iLeft   Left pad
20589      */
20590     setPadding: function(iTop, iRight, iBot, iLeft) {
20591         // this.padding = [iLeft, iRight, iTop, iBot];
20592         if (!iRight && 0 !== iRight) {
20593             this.padding = [iTop, iTop, iTop, iTop];
20594         } else if (!iBot && 0 !== iBot) {
20595             this.padding = [iTop, iRight, iTop, iRight];
20596         } else {
20597             this.padding = [iTop, iRight, iBot, iLeft];
20598         }
20599     },
20600
20601     /**
20602      * Stores the initial placement of the linked element.
20603      * @method setInitialPosition
20604      * @param {int} diffX   the X offset, default 0
20605      * @param {int} diffY   the Y offset, default 0
20606      */
20607     setInitPosition: function(diffX, diffY) {
20608         var el = this.getEl();
20609
20610         if (!this.DDM.verifyEl(el)) {
20611             return;
20612         }
20613
20614         var dx = diffX || 0;
20615         var dy = diffY || 0;
20616
20617         var p = Dom.getXY( el );
20618
20619         this.initPageX = p[0] - dx;
20620         this.initPageY = p[1] - dy;
20621
20622         this.lastPageX = p[0];
20623         this.lastPageY = p[1];
20624
20625
20626         this.setStartPosition(p);
20627     },
20628
20629     /**
20630      * Sets the start position of the element.  This is set when the obj
20631      * is initialized, the reset when a drag is started.
20632      * @method setStartPosition
20633      * @param pos current position (from previous lookup)
20634      * @private
20635      */
20636     setStartPosition: function(pos) {
20637         var p = pos || Dom.getXY( this.getEl() );
20638         this.deltaSetXY = null;
20639
20640         this.startPageX = p[0];
20641         this.startPageY = p[1];
20642     },
20643
20644     /**
20645      * Add this instance to a group of related drag/drop objects.  All
20646      * instances belong to at least one group, and can belong to as many
20647      * groups as needed.
20648      * @method addToGroup
20649      * @param sGroup {string} the name of the group
20650      */
20651     addToGroup: function(sGroup) {
20652         this.groups[sGroup] = true;
20653         this.DDM.regDragDrop(this, sGroup);
20654     },
20655
20656     /**
20657      * Remove's this instance from the supplied interaction group
20658      * @method removeFromGroup
20659      * @param {string}  sGroup  The group to drop
20660      */
20661     removeFromGroup: function(sGroup) {
20662         if (this.groups[sGroup]) {
20663             delete this.groups[sGroup];
20664         }
20665
20666         this.DDM.removeDDFromGroup(this, sGroup);
20667     },
20668
20669     /**
20670      * Allows you to specify that an element other than the linked element
20671      * will be moved with the cursor during a drag
20672      * @method setDragElId
20673      * @param id {string} the id of the element that will be used to initiate the drag
20674      */
20675     setDragElId: function(id) {
20676         this.dragElId = id;
20677     },
20678
20679     /**
20680      * Allows you to specify a child of the linked element that should be
20681      * used to initiate the drag operation.  An example of this would be if
20682      * you have a content div with text and links.  Clicking anywhere in the
20683      * content area would normally start the drag operation.  Use this method
20684      * to specify that an element inside of the content div is the element
20685      * that starts the drag operation.
20686      * @method setHandleElId
20687      * @param id {string} the id of the element that will be used to
20688      * initiate the drag.
20689      */
20690     setHandleElId: function(id) {
20691         if (typeof id !== "string") {
20692             id = Roo.id(id);
20693         }
20694         this.handleElId = id;
20695         this.DDM.regHandle(this.id, id);
20696     },
20697
20698     /**
20699      * Allows you to set an element outside of the linked element as a drag
20700      * handle
20701      * @method setOuterHandleElId
20702      * @param id the id of the element that will be used to initiate the drag
20703      */
20704     setOuterHandleElId: function(id) {
20705         if (typeof id !== "string") {
20706             id = Roo.id(id);
20707         }
20708         Event.on(id, "mousedown",
20709                 this.handleMouseDown, this);
20710         this.setHandleElId(id);
20711
20712         this.hasOuterHandles = true;
20713     },
20714
20715     /**
20716      * Remove all drag and drop hooks for this element
20717      * @method unreg
20718      */
20719     unreg: function() {
20720         Event.un(this.id, "mousedown",
20721                 this.handleMouseDown);
20722         Event.un(this.id, "touchstart",
20723                 this.handleMouseDown);
20724         this._domRef = null;
20725         this.DDM._remove(this);
20726     },
20727
20728     destroy : function(){
20729         this.unreg();
20730     },
20731
20732     /**
20733      * Returns true if this instance is locked, or the drag drop mgr is locked
20734      * (meaning that all drag/drop is disabled on the page.)
20735      * @method isLocked
20736      * @return {boolean} true if this obj or all drag/drop is locked, else
20737      * false
20738      */
20739     isLocked: function() {
20740         return (this.DDM.isLocked() || this.locked);
20741     },
20742
20743     /**
20744      * Fired when this object is clicked
20745      * @method handleMouseDown
20746      * @param {Event} e
20747      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20748      * @private
20749      */
20750     handleMouseDown: function(e, oDD){
20751      
20752         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20753             //Roo.log('not touch/ button !=0');
20754             return;
20755         }
20756         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20757             return; // double touch..
20758         }
20759         
20760
20761         if (this.isLocked()) {
20762             //Roo.log('locked');
20763             return;
20764         }
20765
20766         this.DDM.refreshCache(this.groups);
20767 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20768         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20769         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20770             //Roo.log('no outer handes or not over target');
20771                 // do nothing.
20772         } else {
20773 //            Roo.log('check validator');
20774             if (this.clickValidator(e)) {
20775 //                Roo.log('validate success');
20776                 // set the initial element position
20777                 this.setStartPosition();
20778
20779
20780                 this.b4MouseDown(e);
20781                 this.onMouseDown(e);
20782
20783                 this.DDM.handleMouseDown(e, this);
20784
20785                 this.DDM.stopEvent(e);
20786             } else {
20787
20788
20789             }
20790         }
20791     },
20792
20793     clickValidator: function(e) {
20794         var target = e.getTarget();
20795         return ( this.isValidHandleChild(target) &&
20796                     (this.id == this.handleElId ||
20797                         this.DDM.handleWasClicked(target, this.id)) );
20798     },
20799
20800     /**
20801      * Allows you to specify a tag name that should not start a drag operation
20802      * when clicked.  This is designed to facilitate embedding links within a
20803      * drag handle that do something other than start the drag.
20804      * @method addInvalidHandleType
20805      * @param {string} tagName the type of element to exclude
20806      */
20807     addInvalidHandleType: function(tagName) {
20808         var type = tagName.toUpperCase();
20809         this.invalidHandleTypes[type] = type;
20810     },
20811
20812     /**
20813      * Lets you to specify an element id for a child of a drag handle
20814      * that should not initiate a drag
20815      * @method addInvalidHandleId
20816      * @param {string} id the element id of the element you wish to ignore
20817      */
20818     addInvalidHandleId: function(id) {
20819         if (typeof id !== "string") {
20820             id = Roo.id(id);
20821         }
20822         this.invalidHandleIds[id] = id;
20823     },
20824
20825     /**
20826      * Lets you specify a css class of elements that will not initiate a drag
20827      * @method addInvalidHandleClass
20828      * @param {string} cssClass the class of the elements you wish to ignore
20829      */
20830     addInvalidHandleClass: function(cssClass) {
20831         this.invalidHandleClasses.push(cssClass);
20832     },
20833
20834     /**
20835      * Unsets an excluded tag name set by addInvalidHandleType
20836      * @method removeInvalidHandleType
20837      * @param {string} tagName the type of element to unexclude
20838      */
20839     removeInvalidHandleType: function(tagName) {
20840         var type = tagName.toUpperCase();
20841         // this.invalidHandleTypes[type] = null;
20842         delete this.invalidHandleTypes[type];
20843     },
20844
20845     /**
20846      * Unsets an invalid handle id
20847      * @method removeInvalidHandleId
20848      * @param {string} id the id of the element to re-enable
20849      */
20850     removeInvalidHandleId: function(id) {
20851         if (typeof id !== "string") {
20852             id = Roo.id(id);
20853         }
20854         delete this.invalidHandleIds[id];
20855     },
20856
20857     /**
20858      * Unsets an invalid css class
20859      * @method removeInvalidHandleClass
20860      * @param {string} cssClass the class of the element(s) you wish to
20861      * re-enable
20862      */
20863     removeInvalidHandleClass: function(cssClass) {
20864         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20865             if (this.invalidHandleClasses[i] == cssClass) {
20866                 delete this.invalidHandleClasses[i];
20867             }
20868         }
20869     },
20870
20871     /**
20872      * Checks the tag exclusion list to see if this click should be ignored
20873      * @method isValidHandleChild
20874      * @param {HTMLElement} node the HTMLElement to evaluate
20875      * @return {boolean} true if this is a valid tag type, false if not
20876      */
20877     isValidHandleChild: function(node) {
20878
20879         var valid = true;
20880         // var n = (node.nodeName == "#text") ? node.parentNode : node;
20881         var nodeName;
20882         try {
20883             nodeName = node.nodeName.toUpperCase();
20884         } catch(e) {
20885             nodeName = node.nodeName;
20886         }
20887         valid = valid && !this.invalidHandleTypes[nodeName];
20888         valid = valid && !this.invalidHandleIds[node.id];
20889
20890         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20891             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20892         }
20893
20894
20895         return valid;
20896
20897     },
20898
20899     /**
20900      * Create the array of horizontal tick marks if an interval was specified
20901      * in setXConstraint().
20902      * @method setXTicks
20903      * @private
20904      */
20905     setXTicks: function(iStartX, iTickSize) {
20906         this.xTicks = [];
20907         this.xTickSize = iTickSize;
20908
20909         var tickMap = {};
20910
20911         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20912             if (!tickMap[i]) {
20913                 this.xTicks[this.xTicks.length] = i;
20914                 tickMap[i] = true;
20915             }
20916         }
20917
20918         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20919             if (!tickMap[i]) {
20920                 this.xTicks[this.xTicks.length] = i;
20921                 tickMap[i] = true;
20922             }
20923         }
20924
20925         this.xTicks.sort(this.DDM.numericSort) ;
20926     },
20927
20928     /**
20929      * Create the array of vertical tick marks if an interval was specified in
20930      * setYConstraint().
20931      * @method setYTicks
20932      * @private
20933      */
20934     setYTicks: function(iStartY, iTickSize) {
20935         this.yTicks = [];
20936         this.yTickSize = iTickSize;
20937
20938         var tickMap = {};
20939
20940         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20941             if (!tickMap[i]) {
20942                 this.yTicks[this.yTicks.length] = i;
20943                 tickMap[i] = true;
20944             }
20945         }
20946
20947         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20948             if (!tickMap[i]) {
20949                 this.yTicks[this.yTicks.length] = i;
20950                 tickMap[i] = true;
20951             }
20952         }
20953
20954         this.yTicks.sort(this.DDM.numericSort) ;
20955     },
20956
20957     /**
20958      * By default, the element can be dragged any place on the screen.  Use
20959      * this method to limit the horizontal travel of the element.  Pass in
20960      * 0,0 for the parameters if you want to lock the drag to the y axis.
20961      * @method setXConstraint
20962      * @param {int} iLeft the number of pixels the element can move to the left
20963      * @param {int} iRight the number of pixels the element can move to the
20964      * right
20965      * @param {int} iTickSize optional parameter for specifying that the
20966      * element
20967      * should move iTickSize pixels at a time.
20968      */
20969     setXConstraint: function(iLeft, iRight, iTickSize) {
20970         this.leftConstraint = iLeft;
20971         this.rightConstraint = iRight;
20972
20973         this.minX = this.initPageX - iLeft;
20974         this.maxX = this.initPageX + iRight;
20975         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20976
20977         this.constrainX = true;
20978     },
20979
20980     /**
20981      * Clears any constraints applied to this instance.  Also clears ticks
20982      * since they can't exist independent of a constraint at this time.
20983      * @method clearConstraints
20984      */
20985     clearConstraints: function() {
20986         this.constrainX = false;
20987         this.constrainY = false;
20988         this.clearTicks();
20989     },
20990
20991     /**
20992      * Clears any tick interval defined for this instance
20993      * @method clearTicks
20994      */
20995     clearTicks: function() {
20996         this.xTicks = null;
20997         this.yTicks = null;
20998         this.xTickSize = 0;
20999         this.yTickSize = 0;
21000     },
21001
21002     /**
21003      * By default, the element can be dragged any place on the screen.  Set
21004      * this to limit the vertical travel of the element.  Pass in 0,0 for the
21005      * parameters if you want to lock the drag to the x axis.
21006      * @method setYConstraint
21007      * @param {int} iUp the number of pixels the element can move up
21008      * @param {int} iDown the number of pixels the element can move down
21009      * @param {int} iTickSize optional parameter for specifying that the
21010      * element should move iTickSize pixels at a time.
21011      */
21012     setYConstraint: function(iUp, iDown, iTickSize) {
21013         this.topConstraint = iUp;
21014         this.bottomConstraint = iDown;
21015
21016         this.minY = this.initPageY - iUp;
21017         this.maxY = this.initPageY + iDown;
21018         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
21019
21020         this.constrainY = true;
21021
21022     },
21023
21024     /**
21025      * resetConstraints must be called if you manually reposition a dd element.
21026      * @method resetConstraints
21027      * @param {boolean} maintainOffset
21028      */
21029     resetConstraints: function() {
21030
21031
21032         // Maintain offsets if necessary
21033         if (this.initPageX || this.initPageX === 0) {
21034             // figure out how much this thing has moved
21035             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
21036             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
21037
21038             this.setInitPosition(dx, dy);
21039
21040         // This is the first time we have detected the element's position
21041         } else {
21042             this.setInitPosition();
21043         }
21044
21045         if (this.constrainX) {
21046             this.setXConstraint( this.leftConstraint,
21047                                  this.rightConstraint,
21048                                  this.xTickSize        );
21049         }
21050
21051         if (this.constrainY) {
21052             this.setYConstraint( this.topConstraint,
21053                                  this.bottomConstraint,
21054                                  this.yTickSize         );
21055         }
21056     },
21057
21058     /**
21059      * Normally the drag element is moved pixel by pixel, but we can specify
21060      * that it move a number of pixels at a time.  This method resolves the
21061      * location when we have it set up like this.
21062      * @method getTick
21063      * @param {int} val where we want to place the object
21064      * @param {int[]} tickArray sorted array of valid points
21065      * @return {int} the closest tick
21066      * @private
21067      */
21068     getTick: function(val, tickArray) {
21069
21070         if (!tickArray) {
21071             // If tick interval is not defined, it is effectively 1 pixel,
21072             // so we return the value passed to us.
21073             return val;
21074         } else if (tickArray[0] >= val) {
21075             // The value is lower than the first tick, so we return the first
21076             // tick.
21077             return tickArray[0];
21078         } else {
21079             for (var i=0, len=tickArray.length; i<len; ++i) {
21080                 var next = i + 1;
21081                 if (tickArray[next] && tickArray[next] >= val) {
21082                     var diff1 = val - tickArray[i];
21083                     var diff2 = tickArray[next] - val;
21084                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
21085                 }
21086             }
21087
21088             // The value is larger than the last tick, so we return the last
21089             // tick.
21090             return tickArray[tickArray.length - 1];
21091         }
21092     },
21093
21094     /**
21095      * toString method
21096      * @method toString
21097      * @return {string} string representation of the dd obj
21098      */
21099     toString: function() {
21100         return ("DragDrop " + this.id);
21101     }
21102
21103 });
21104
21105 })();
21106 /*
21107  * Based on:
21108  * Ext JS Library 1.1.1
21109  * Copyright(c) 2006-2007, Ext JS, LLC.
21110  *
21111  * Originally Released Under LGPL - original licence link has changed is not relivant.
21112  *
21113  * Fork - LGPL
21114  * <script type="text/javascript">
21115  */
21116
21117
21118 /**
21119  * The drag and drop utility provides a framework for building drag and drop
21120  * applications.  In addition to enabling drag and drop for specific elements,
21121  * the drag and drop elements are tracked by the manager class, and the
21122  * interactions between the various elements are tracked during the drag and
21123  * the implementing code is notified about these important moments.
21124  */
21125
21126 // Only load the library once.  Rewriting the manager class would orphan
21127 // existing drag and drop instances.
21128 if (!Roo.dd.DragDropMgr) {
21129
21130 /**
21131  * @class Roo.dd.DragDropMgr
21132  * DragDropMgr is a singleton that tracks the element interaction for
21133  * all DragDrop items in the window.  Generally, you will not call
21134  * this class directly, but it does have helper methods that could
21135  * be useful in your DragDrop implementations.
21136  * @static
21137  */
21138 Roo.dd.DragDropMgr = function() {
21139
21140     var Event = Roo.EventManager;
21141
21142     return {
21143
21144         /**
21145          * Two dimensional Array of registered DragDrop objects.  The first
21146          * dimension is the DragDrop item group, the second the DragDrop
21147          * object.
21148          * @property ids
21149          * @type {string: string}
21150          * @private
21151          * @static
21152          */
21153         ids: {},
21154
21155         /**
21156          * Array of element ids defined as drag handles.  Used to determine
21157          * if the element that generated the mousedown event is actually the
21158          * handle and not the html element itself.
21159          * @property handleIds
21160          * @type {string: string}
21161          * @private
21162          * @static
21163          */
21164         handleIds: {},
21165
21166         /**
21167          * the DragDrop object that is currently being dragged
21168          * @property dragCurrent
21169          * @type DragDrop
21170          * @private
21171          * @static
21172          **/
21173         dragCurrent: null,
21174
21175         /**
21176          * the DragDrop object(s) that are being hovered over
21177          * @property dragOvers
21178          * @type Array
21179          * @private
21180          * @static
21181          */
21182         dragOvers: {},
21183
21184         /**
21185          * the X distance between the cursor and the object being dragged
21186          * @property deltaX
21187          * @type int
21188          * @private
21189          * @static
21190          */
21191         deltaX: 0,
21192
21193         /**
21194          * the Y distance between the cursor and the object being dragged
21195          * @property deltaY
21196          * @type int
21197          * @private
21198          * @static
21199          */
21200         deltaY: 0,
21201
21202         /**
21203          * Flag to determine if we should prevent the default behavior of the
21204          * events we define. By default this is true, but this can be set to
21205          * false if you need the default behavior (not recommended)
21206          * @property preventDefault
21207          * @type boolean
21208          * @static
21209          */
21210         preventDefault: true,
21211
21212         /**
21213          * Flag to determine if we should stop the propagation of the events
21214          * we generate. This is true by default but you may want to set it to
21215          * false if the html element contains other features that require the
21216          * mouse click.
21217          * @property stopPropagation
21218          * @type boolean
21219          * @static
21220          */
21221         stopPropagation: true,
21222
21223         /**
21224          * Internal flag that is set to true when drag and drop has been
21225          * intialized
21226          * @property initialized
21227          * @private
21228          * @static
21229          */
21230         initalized: false,
21231
21232         /**
21233          * All drag and drop can be disabled.
21234          * @property locked
21235          * @private
21236          * @static
21237          */
21238         locked: false,
21239
21240         /**
21241          * Called the first time an element is registered.
21242          * @method init
21243          * @private
21244          * @static
21245          */
21246         init: function() {
21247             this.initialized = true;
21248         },
21249
21250         /**
21251          * In point mode, drag and drop interaction is defined by the
21252          * location of the cursor during the drag/drop
21253          * @property POINT
21254          * @type int
21255          * @static
21256          */
21257         POINT: 0,
21258
21259         /**
21260          * In intersect mode, drag and drop interactio nis defined by the
21261          * overlap of two or more drag and drop objects.
21262          * @property INTERSECT
21263          * @type int
21264          * @static
21265          */
21266         INTERSECT: 1,
21267
21268         /**
21269          * The current drag and drop mode.  Default: POINT
21270          * @property mode
21271          * @type int
21272          * @static
21273          */
21274         mode: 0,
21275
21276         /**
21277          * Runs method on all drag and drop objects
21278          * @method _execOnAll
21279          * @private
21280          * @static
21281          */
21282         _execOnAll: function(sMethod, args) {
21283             for (var i in this.ids) {
21284                 for (var j in this.ids[i]) {
21285                     var oDD = this.ids[i][j];
21286                     if (! this.isTypeOfDD(oDD)) {
21287                         continue;
21288                     }
21289                     oDD[sMethod].apply(oDD, args);
21290                 }
21291             }
21292         },
21293
21294         /**
21295          * Drag and drop initialization.  Sets up the global event handlers
21296          * @method _onLoad
21297          * @private
21298          * @static
21299          */
21300         _onLoad: function() {
21301
21302             this.init();
21303
21304             if (!Roo.isTouch) {
21305                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
21306                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21307             }
21308             Event.on(document, "touchend",   this.handleMouseUp, this, true);
21309             Event.on(document, "touchmove", this.handleMouseMove, this, true);
21310             
21311             Event.on(window,   "unload",    this._onUnload, this, true);
21312             Event.on(window,   "resize",    this._onResize, this, true);
21313             // Event.on(window,   "mouseout",    this._test);
21314
21315         },
21316
21317         /**
21318          * Reset constraints on all drag and drop objs
21319          * @method _onResize
21320          * @private
21321          * @static
21322          */
21323         _onResize: function(e) {
21324             this._execOnAll("resetConstraints", []);
21325         },
21326
21327         /**
21328          * Lock all drag and drop functionality
21329          * @method lock
21330          * @static
21331          */
21332         lock: function() { this.locked = true; },
21333
21334         /**
21335          * Unlock all drag and drop functionality
21336          * @method unlock
21337          * @static
21338          */
21339         unlock: function() { this.locked = false; },
21340
21341         /**
21342          * Is drag and drop locked?
21343          * @method isLocked
21344          * @return {boolean} True if drag and drop is locked, false otherwise.
21345          * @static
21346          */
21347         isLocked: function() { return this.locked; },
21348
21349         /**
21350          * Location cache that is set for all drag drop objects when a drag is
21351          * initiated, cleared when the drag is finished.
21352          * @property locationCache
21353          * @private
21354          * @static
21355          */
21356         locationCache: {},
21357
21358         /**
21359          * Set useCache to false if you want to force object the lookup of each
21360          * drag and drop linked element constantly during a drag.
21361          * @property useCache
21362          * @type boolean
21363          * @static
21364          */
21365         useCache: true,
21366
21367         /**
21368          * The number of pixels that the mouse needs to move after the
21369          * mousedown before the drag is initiated.  Default=3;
21370          * @property clickPixelThresh
21371          * @type int
21372          * @static
21373          */
21374         clickPixelThresh: 3,
21375
21376         /**
21377          * The number of milliseconds after the mousedown event to initiate the
21378          * drag if we don't get a mouseup event. Default=1000
21379          * @property clickTimeThresh
21380          * @type int
21381          * @static
21382          */
21383         clickTimeThresh: 350,
21384
21385         /**
21386          * Flag that indicates that either the drag pixel threshold or the
21387          * mousdown time threshold has been met
21388          * @property dragThreshMet
21389          * @type boolean
21390          * @private
21391          * @static
21392          */
21393         dragThreshMet: false,
21394
21395         /**
21396          * Timeout used for the click time threshold
21397          * @property clickTimeout
21398          * @type Object
21399          * @private
21400          * @static
21401          */
21402         clickTimeout: null,
21403
21404         /**
21405          * The X position of the mousedown event stored for later use when a
21406          * drag threshold is met.
21407          * @property startX
21408          * @type int
21409          * @private
21410          * @static
21411          */
21412         startX: 0,
21413
21414         /**
21415          * The Y position of the mousedown event stored for later use when a
21416          * drag threshold is met.
21417          * @property startY
21418          * @type int
21419          * @private
21420          * @static
21421          */
21422         startY: 0,
21423
21424         /**
21425          * Each DragDrop instance must be registered with the DragDropMgr.
21426          * This is executed in DragDrop.init()
21427          * @method regDragDrop
21428          * @param {DragDrop} oDD the DragDrop object to register
21429          * @param {String} sGroup the name of the group this element belongs to
21430          * @static
21431          */
21432         regDragDrop: function(oDD, sGroup) {
21433             if (!this.initialized) { this.init(); }
21434
21435             if (!this.ids[sGroup]) {
21436                 this.ids[sGroup] = {};
21437             }
21438             this.ids[sGroup][oDD.id] = oDD;
21439         },
21440
21441         /**
21442          * Removes the supplied dd instance from the supplied group. Executed
21443          * by DragDrop.removeFromGroup, so don't call this function directly.
21444          * @method removeDDFromGroup
21445          * @private
21446          * @static
21447          */
21448         removeDDFromGroup: function(oDD, sGroup) {
21449             if (!this.ids[sGroup]) {
21450                 this.ids[sGroup] = {};
21451             }
21452
21453             var obj = this.ids[sGroup];
21454             if (obj && obj[oDD.id]) {
21455                 delete obj[oDD.id];
21456             }
21457         },
21458
21459         /**
21460          * Unregisters a drag and drop item.  This is executed in
21461          * DragDrop.unreg, use that method instead of calling this directly.
21462          * @method _remove
21463          * @private
21464          * @static
21465          */
21466         _remove: function(oDD) {
21467             for (var g in oDD.groups) {
21468                 if (g && this.ids[g][oDD.id]) {
21469                     delete this.ids[g][oDD.id];
21470                 }
21471             }
21472             delete this.handleIds[oDD.id];
21473         },
21474
21475         /**
21476          * Each DragDrop handle element must be registered.  This is done
21477          * automatically when executing DragDrop.setHandleElId()
21478          * @method regHandle
21479          * @param {String} sDDId the DragDrop id this element is a handle for
21480          * @param {String} sHandleId the id of the element that is the drag
21481          * handle
21482          * @static
21483          */
21484         regHandle: function(sDDId, sHandleId) {
21485             if (!this.handleIds[sDDId]) {
21486                 this.handleIds[sDDId] = {};
21487             }
21488             this.handleIds[sDDId][sHandleId] = sHandleId;
21489         },
21490
21491         /**
21492          * Utility function to determine if a given element has been
21493          * registered as a drag drop item.
21494          * @method isDragDrop
21495          * @param {String} id the element id to check
21496          * @return {boolean} true if this element is a DragDrop item,
21497          * false otherwise
21498          * @static
21499          */
21500         isDragDrop: function(id) {
21501             return ( this.getDDById(id) ) ? true : false;
21502         },
21503
21504         /**
21505          * Returns the drag and drop instances that are in all groups the
21506          * passed in instance belongs to.
21507          * @method getRelated
21508          * @param {DragDrop} p_oDD the obj to get related data for
21509          * @param {boolean} bTargetsOnly if true, only return targetable objs
21510          * @return {DragDrop[]} the related instances
21511          * @static
21512          */
21513         getRelated: function(p_oDD, bTargetsOnly) {
21514             var oDDs = [];
21515             for (var i in p_oDD.groups) {
21516                 for (j in this.ids[i]) {
21517                     var dd = this.ids[i][j];
21518                     if (! this.isTypeOfDD(dd)) {
21519                         continue;
21520                     }
21521                     if (!bTargetsOnly || dd.isTarget) {
21522                         oDDs[oDDs.length] = dd;
21523                     }
21524                 }
21525             }
21526
21527             return oDDs;
21528         },
21529
21530         /**
21531          * Returns true if the specified dd target is a legal target for
21532          * the specifice drag obj
21533          * @method isLegalTarget
21534          * @param {DragDrop} the drag obj
21535          * @param {DragDrop} the target
21536          * @return {boolean} true if the target is a legal target for the
21537          * dd obj
21538          * @static
21539          */
21540         isLegalTarget: function (oDD, oTargetDD) {
21541             var targets = this.getRelated(oDD, true);
21542             for (var i=0, len=targets.length;i<len;++i) {
21543                 if (targets[i].id == oTargetDD.id) {
21544                     return true;
21545                 }
21546             }
21547
21548             return false;
21549         },
21550
21551         /**
21552          * My goal is to be able to transparently determine if an object is
21553          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21554          * returns "object", oDD.constructor.toString() always returns
21555          * "DragDrop" and not the name of the subclass.  So for now it just
21556          * evaluates a well-known variable in DragDrop.
21557          * @method isTypeOfDD
21558          * @param {Object} the object to evaluate
21559          * @return {boolean} true if typeof oDD = DragDrop
21560          * @static
21561          */
21562         isTypeOfDD: function (oDD) {
21563             return (oDD && oDD.__ygDragDrop);
21564         },
21565
21566         /**
21567          * Utility function to determine if a given element has been
21568          * registered as a drag drop handle for the given Drag Drop object.
21569          * @method isHandle
21570          * @param {String} id the element id to check
21571          * @return {boolean} true if this element is a DragDrop handle, false
21572          * otherwise
21573          * @static
21574          */
21575         isHandle: function(sDDId, sHandleId) {
21576             return ( this.handleIds[sDDId] &&
21577                             this.handleIds[sDDId][sHandleId] );
21578         },
21579
21580         /**
21581          * Returns the DragDrop instance for a given id
21582          * @method getDDById
21583          * @param {String} id the id of the DragDrop object
21584          * @return {DragDrop} the drag drop object, null if it is not found
21585          * @static
21586          */
21587         getDDById: function(id) {
21588             for (var i in this.ids) {
21589                 if (this.ids[i][id]) {
21590                     return this.ids[i][id];
21591                 }
21592             }
21593             return null;
21594         },
21595
21596         /**
21597          * Fired after a registered DragDrop object gets the mousedown event.
21598          * Sets up the events required to track the object being dragged
21599          * @method handleMouseDown
21600          * @param {Event} e the event
21601          * @param oDD the DragDrop object being dragged
21602          * @private
21603          * @static
21604          */
21605         handleMouseDown: function(e, oDD) {
21606             if(Roo.QuickTips){
21607                 Roo.QuickTips.disable();
21608             }
21609             this.currentTarget = e.getTarget();
21610
21611             this.dragCurrent = oDD;
21612
21613             var el = oDD.getEl();
21614
21615             // track start position
21616             this.startX = e.getPageX();
21617             this.startY = e.getPageY();
21618
21619             this.deltaX = this.startX - el.offsetLeft;
21620             this.deltaY = this.startY - el.offsetTop;
21621
21622             this.dragThreshMet = false;
21623
21624             this.clickTimeout = setTimeout(
21625                     function() {
21626                         var DDM = Roo.dd.DDM;
21627                         DDM.startDrag(DDM.startX, DDM.startY);
21628                     },
21629                     this.clickTimeThresh );
21630         },
21631
21632         /**
21633          * Fired when either the drag pixel threshol or the mousedown hold
21634          * time threshold has been met.
21635          * @method startDrag
21636          * @param x {int} the X position of the original mousedown
21637          * @param y {int} the Y position of the original mousedown
21638          * @static
21639          */
21640         startDrag: function(x, y) {
21641             clearTimeout(this.clickTimeout);
21642             if (this.dragCurrent) {
21643                 this.dragCurrent.b4StartDrag(x, y);
21644                 this.dragCurrent.startDrag(x, y);
21645             }
21646             this.dragThreshMet = true;
21647         },
21648
21649         /**
21650          * Internal function to handle the mouseup event.  Will be invoked
21651          * from the context of the document.
21652          * @method handleMouseUp
21653          * @param {Event} e the event
21654          * @private
21655          * @static
21656          */
21657         handleMouseUp: function(e) {
21658
21659             if(Roo.QuickTips){
21660                 Roo.QuickTips.enable();
21661             }
21662             if (! this.dragCurrent) {
21663                 return;
21664             }
21665
21666             clearTimeout(this.clickTimeout);
21667
21668             if (this.dragThreshMet) {
21669                 this.fireEvents(e, true);
21670             } else {
21671             }
21672
21673             this.stopDrag(e);
21674
21675             this.stopEvent(e);
21676         },
21677
21678         /**
21679          * Utility to stop event propagation and event default, if these
21680          * features are turned on.
21681          * @method stopEvent
21682          * @param {Event} e the event as returned by this.getEvent()
21683          * @static
21684          */
21685         stopEvent: function(e){
21686             if(this.stopPropagation) {
21687                 e.stopPropagation();
21688             }
21689
21690             if (this.preventDefault) {
21691                 e.preventDefault();
21692             }
21693         },
21694
21695         /**
21696          * Internal function to clean up event handlers after the drag
21697          * operation is complete
21698          * @method stopDrag
21699          * @param {Event} e the event
21700          * @private
21701          * @static
21702          */
21703         stopDrag: function(e) {
21704             // Fire the drag end event for the item that was dragged
21705             if (this.dragCurrent) {
21706                 if (this.dragThreshMet) {
21707                     this.dragCurrent.b4EndDrag(e);
21708                     this.dragCurrent.endDrag(e);
21709                 }
21710
21711                 this.dragCurrent.onMouseUp(e);
21712             }
21713
21714             this.dragCurrent = null;
21715             this.dragOvers = {};
21716         },
21717
21718         /**
21719          * Internal function to handle the mousemove event.  Will be invoked
21720          * from the context of the html element.
21721          *
21722          * @TODO figure out what we can do about mouse events lost when the
21723          * user drags objects beyond the window boundary.  Currently we can
21724          * detect this in internet explorer by verifying that the mouse is
21725          * down during the mousemove event.  Firefox doesn't give us the
21726          * button state on the mousemove event.
21727          * @method handleMouseMove
21728          * @param {Event} e the event
21729          * @private
21730          * @static
21731          */
21732         handleMouseMove: function(e) {
21733             if (! this.dragCurrent) {
21734                 return true;
21735             }
21736
21737             // var button = e.which || e.button;
21738
21739             // check for IE mouseup outside of page boundary
21740             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21741                 this.stopEvent(e);
21742                 return this.handleMouseUp(e);
21743             }
21744
21745             if (!this.dragThreshMet) {
21746                 var diffX = Math.abs(this.startX - e.getPageX());
21747                 var diffY = Math.abs(this.startY - e.getPageY());
21748                 if (diffX > this.clickPixelThresh ||
21749                             diffY > this.clickPixelThresh) {
21750                     this.startDrag(this.startX, this.startY);
21751                 }
21752             }
21753
21754             if (this.dragThreshMet) {
21755                 this.dragCurrent.b4Drag(e);
21756                 this.dragCurrent.onDrag(e);
21757                 if(!this.dragCurrent.moveOnly){
21758                     this.fireEvents(e, false);
21759                 }
21760             }
21761
21762             this.stopEvent(e);
21763
21764             return true;
21765         },
21766
21767         /**
21768          * Iterates over all of the DragDrop elements to find ones we are
21769          * hovering over or dropping on
21770          * @method fireEvents
21771          * @param {Event} e the event
21772          * @param {boolean} isDrop is this a drop op or a mouseover op?
21773          * @private
21774          * @static
21775          */
21776         fireEvents: function(e, isDrop) {
21777             var dc = this.dragCurrent;
21778
21779             // If the user did the mouse up outside of the window, we could
21780             // get here even though we have ended the drag.
21781             if (!dc || dc.isLocked()) {
21782                 return;
21783             }
21784
21785             var pt = e.getPoint();
21786
21787             // cache the previous dragOver array
21788             var oldOvers = [];
21789
21790             var outEvts   = [];
21791             var overEvts  = [];
21792             var dropEvts  = [];
21793             var enterEvts = [];
21794
21795             // Check to see if the object(s) we were hovering over is no longer
21796             // being hovered over so we can fire the onDragOut event
21797             for (var i in this.dragOvers) {
21798
21799                 var ddo = this.dragOvers[i];
21800
21801                 if (! this.isTypeOfDD(ddo)) {
21802                     continue;
21803                 }
21804
21805                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21806                     outEvts.push( ddo );
21807                 }
21808
21809                 oldOvers[i] = true;
21810                 delete this.dragOvers[i];
21811             }
21812
21813             for (var sGroup in dc.groups) {
21814
21815                 if ("string" != typeof sGroup) {
21816                     continue;
21817                 }
21818
21819                 for (i in this.ids[sGroup]) {
21820                     var oDD = this.ids[sGroup][i];
21821                     if (! this.isTypeOfDD(oDD)) {
21822                         continue;
21823                     }
21824
21825                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21826                         if (this.isOverTarget(pt, oDD, this.mode)) {
21827                             // look for drop interactions
21828                             if (isDrop) {
21829                                 dropEvts.push( oDD );
21830                             // look for drag enter and drag over interactions
21831                             } else {
21832
21833                                 // initial drag over: dragEnter fires
21834                                 if (!oldOvers[oDD.id]) {
21835                                     enterEvts.push( oDD );
21836                                 // subsequent drag overs: dragOver fires
21837                                 } else {
21838                                     overEvts.push( oDD );
21839                                 }
21840
21841                                 this.dragOvers[oDD.id] = oDD;
21842                             }
21843                         }
21844                     }
21845                 }
21846             }
21847
21848             if (this.mode) {
21849                 if (outEvts.length) {
21850                     dc.b4DragOut(e, outEvts);
21851                     dc.onDragOut(e, outEvts);
21852                 }
21853
21854                 if (enterEvts.length) {
21855                     dc.onDragEnter(e, enterEvts);
21856                 }
21857
21858                 if (overEvts.length) {
21859                     dc.b4DragOver(e, overEvts);
21860                     dc.onDragOver(e, overEvts);
21861                 }
21862
21863                 if (dropEvts.length) {
21864                     dc.b4DragDrop(e, dropEvts);
21865                     dc.onDragDrop(e, dropEvts);
21866                 }
21867
21868             } else {
21869                 // fire dragout events
21870                 var len = 0;
21871                 for (i=0, len=outEvts.length; i<len; ++i) {
21872                     dc.b4DragOut(e, outEvts[i].id);
21873                     dc.onDragOut(e, outEvts[i].id);
21874                 }
21875
21876                 // fire enter events
21877                 for (i=0,len=enterEvts.length; i<len; ++i) {
21878                     // dc.b4DragEnter(e, oDD.id);
21879                     dc.onDragEnter(e, enterEvts[i].id);
21880                 }
21881
21882                 // fire over events
21883                 for (i=0,len=overEvts.length; i<len; ++i) {
21884                     dc.b4DragOver(e, overEvts[i].id);
21885                     dc.onDragOver(e, overEvts[i].id);
21886                 }
21887
21888                 // fire drop events
21889                 for (i=0, len=dropEvts.length; i<len; ++i) {
21890                     dc.b4DragDrop(e, dropEvts[i].id);
21891                     dc.onDragDrop(e, dropEvts[i].id);
21892                 }
21893
21894             }
21895
21896             // notify about a drop that did not find a target
21897             if (isDrop && !dropEvts.length) {
21898                 dc.onInvalidDrop(e);
21899             }
21900
21901         },
21902
21903         /**
21904          * Helper function for getting the best match from the list of drag
21905          * and drop objects returned by the drag and drop events when we are
21906          * in INTERSECT mode.  It returns either the first object that the
21907          * cursor is over, or the object that has the greatest overlap with
21908          * the dragged element.
21909          * @method getBestMatch
21910          * @param  {DragDrop[]} dds The array of drag and drop objects
21911          * targeted
21912          * @return {DragDrop}       The best single match
21913          * @static
21914          */
21915         getBestMatch: function(dds) {
21916             var winner = null;
21917             // Return null if the input is not what we expect
21918             //if (!dds || !dds.length || dds.length == 0) {
21919                // winner = null;
21920             // If there is only one item, it wins
21921             //} else if (dds.length == 1) {
21922
21923             var len = dds.length;
21924
21925             if (len == 1) {
21926                 winner = dds[0];
21927             } else {
21928                 // Loop through the targeted items
21929                 for (var i=0; i<len; ++i) {
21930                     var dd = dds[i];
21931                     // If the cursor is over the object, it wins.  If the
21932                     // cursor is over multiple matches, the first one we come
21933                     // to wins.
21934                     if (dd.cursorIsOver) {
21935                         winner = dd;
21936                         break;
21937                     // Otherwise the object with the most overlap wins
21938                     } else {
21939                         if (!winner ||
21940                             winner.overlap.getArea() < dd.overlap.getArea()) {
21941                             winner = dd;
21942                         }
21943                     }
21944                 }
21945             }
21946
21947             return winner;
21948         },
21949
21950         /**
21951          * Refreshes the cache of the top-left and bottom-right points of the
21952          * drag and drop objects in the specified group(s).  This is in the
21953          * format that is stored in the drag and drop instance, so typical
21954          * usage is:
21955          * <code>
21956          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21957          * </code>
21958          * Alternatively:
21959          * <code>
21960          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21961          * </code>
21962          * @TODO this really should be an indexed array.  Alternatively this
21963          * method could accept both.
21964          * @method refreshCache
21965          * @param {Object} groups an associative array of groups to refresh
21966          * @static
21967          */
21968         refreshCache: function(groups) {
21969             for (var sGroup in groups) {
21970                 if ("string" != typeof sGroup) {
21971                     continue;
21972                 }
21973                 for (var i in this.ids[sGroup]) {
21974                     var oDD = this.ids[sGroup][i];
21975
21976                     if (this.isTypeOfDD(oDD)) {
21977                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21978                         var loc = this.getLocation(oDD);
21979                         if (loc) {
21980                             this.locationCache[oDD.id] = loc;
21981                         } else {
21982                             delete this.locationCache[oDD.id];
21983                             // this will unregister the drag and drop object if
21984                             // the element is not in a usable state
21985                             // oDD.unreg();
21986                         }
21987                     }
21988                 }
21989             }
21990         },
21991
21992         /**
21993          * This checks to make sure an element exists and is in the DOM.  The
21994          * main purpose is to handle cases where innerHTML is used to remove
21995          * drag and drop objects from the DOM.  IE provides an 'unspecified
21996          * error' when trying to access the offsetParent of such an element
21997          * @method verifyEl
21998          * @param {HTMLElement} el the element to check
21999          * @return {boolean} true if the element looks usable
22000          * @static
22001          */
22002         verifyEl: function(el) {
22003             if (el) {
22004                 var parent;
22005                 if(Roo.isIE){
22006                     try{
22007                         parent = el.offsetParent;
22008                     }catch(e){}
22009                 }else{
22010                     parent = el.offsetParent;
22011                 }
22012                 if (parent) {
22013                     return true;
22014                 }
22015             }
22016
22017             return false;
22018         },
22019
22020         /**
22021          * Returns a Region object containing the drag and drop element's position
22022          * and size, including the padding configured for it
22023          * @method getLocation
22024          * @param {DragDrop} oDD the drag and drop object to get the
22025          *                       location for
22026          * @return {Roo.lib.Region} a Region object representing the total area
22027          *                             the element occupies, including any padding
22028          *                             the instance is configured for.
22029          * @static
22030          */
22031         getLocation: function(oDD) {
22032             if (! this.isTypeOfDD(oDD)) {
22033                 return null;
22034             }
22035
22036             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
22037
22038             try {
22039                 pos= Roo.lib.Dom.getXY(el);
22040             } catch (e) { }
22041
22042             if (!pos) {
22043                 return null;
22044             }
22045
22046             x1 = pos[0];
22047             x2 = x1 + el.offsetWidth;
22048             y1 = pos[1];
22049             y2 = y1 + el.offsetHeight;
22050
22051             t = y1 - oDD.padding[0];
22052             r = x2 + oDD.padding[1];
22053             b = y2 + oDD.padding[2];
22054             l = x1 - oDD.padding[3];
22055
22056             return new Roo.lib.Region( t, r, b, l );
22057         },
22058
22059         /**
22060          * Checks the cursor location to see if it over the target
22061          * @method isOverTarget
22062          * @param {Roo.lib.Point} pt The point to evaluate
22063          * @param {DragDrop} oTarget the DragDrop object we are inspecting
22064          * @return {boolean} true if the mouse is over the target
22065          * @private
22066          * @static
22067          */
22068         isOverTarget: function(pt, oTarget, intersect) {
22069             // use cache if available
22070             var loc = this.locationCache[oTarget.id];
22071             if (!loc || !this.useCache) {
22072                 loc = this.getLocation(oTarget);
22073                 this.locationCache[oTarget.id] = loc;
22074
22075             }
22076
22077             if (!loc) {
22078                 return false;
22079             }
22080
22081             oTarget.cursorIsOver = loc.contains( pt );
22082
22083             // DragDrop is using this as a sanity check for the initial mousedown
22084             // in this case we are done.  In POINT mode, if the drag obj has no
22085             // contraints, we are also done. Otherwise we need to evaluate the
22086             // location of the target as related to the actual location of the
22087             // dragged element.
22088             var dc = this.dragCurrent;
22089             if (!dc || !dc.getTargetCoord ||
22090                     (!intersect && !dc.constrainX && !dc.constrainY)) {
22091                 return oTarget.cursorIsOver;
22092             }
22093
22094             oTarget.overlap = null;
22095
22096             // Get the current location of the drag element, this is the
22097             // location of the mouse event less the delta that represents
22098             // where the original mousedown happened on the element.  We
22099             // need to consider constraints and ticks as well.
22100             var pos = dc.getTargetCoord(pt.x, pt.y);
22101
22102             var el = dc.getDragEl();
22103             var curRegion = new Roo.lib.Region( pos.y,
22104                                                    pos.x + el.offsetWidth,
22105                                                    pos.y + el.offsetHeight,
22106                                                    pos.x );
22107
22108             var overlap = curRegion.intersect(loc);
22109
22110             if (overlap) {
22111                 oTarget.overlap = overlap;
22112                 return (intersect) ? true : oTarget.cursorIsOver;
22113             } else {
22114                 return false;
22115             }
22116         },
22117
22118         /**
22119          * unload event handler
22120          * @method _onUnload
22121          * @private
22122          * @static
22123          */
22124         _onUnload: function(e, me) {
22125             Roo.dd.DragDropMgr.unregAll();
22126         },
22127
22128         /**
22129          * Cleans up the drag and drop events and objects.
22130          * @method unregAll
22131          * @private
22132          * @static
22133          */
22134         unregAll: function() {
22135
22136             if (this.dragCurrent) {
22137                 this.stopDrag();
22138                 this.dragCurrent = null;
22139             }
22140
22141             this._execOnAll("unreg", []);
22142
22143             for (i in this.elementCache) {
22144                 delete this.elementCache[i];
22145             }
22146
22147             this.elementCache = {};
22148             this.ids = {};
22149         },
22150
22151         /**
22152          * A cache of DOM elements
22153          * @property elementCache
22154          * @private
22155          * @static
22156          */
22157         elementCache: {},
22158
22159         /**
22160          * Get the wrapper for the DOM element specified
22161          * @method getElWrapper
22162          * @param {String} id the id of the element to get
22163          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
22164          * @private
22165          * @deprecated This wrapper isn't that useful
22166          * @static
22167          */
22168         getElWrapper: function(id) {
22169             var oWrapper = this.elementCache[id];
22170             if (!oWrapper || !oWrapper.el) {
22171                 oWrapper = this.elementCache[id] =
22172                     new this.ElementWrapper(Roo.getDom(id));
22173             }
22174             return oWrapper;
22175         },
22176
22177         /**
22178          * Returns the actual DOM element
22179          * @method getElement
22180          * @param {String} id the id of the elment to get
22181          * @return {Object} The element
22182          * @deprecated use Roo.getDom instead
22183          * @static
22184          */
22185         getElement: function(id) {
22186             return Roo.getDom(id);
22187         },
22188
22189         /**
22190          * Returns the style property for the DOM element (i.e.,
22191          * document.getElById(id).style)
22192          * @method getCss
22193          * @param {String} id the id of the elment to get
22194          * @return {Object} The style property of the element
22195          * @deprecated use Roo.getDom instead
22196          * @static
22197          */
22198         getCss: function(id) {
22199             var el = Roo.getDom(id);
22200             return (el) ? el.style : null;
22201         },
22202
22203         /**
22204          * Inner class for cached elements
22205          * @class DragDropMgr.ElementWrapper
22206          * @for DragDropMgr
22207          * @private
22208          * @deprecated
22209          */
22210         ElementWrapper: function(el) {
22211                 /**
22212                  * The element
22213                  * @property el
22214                  */
22215                 this.el = el || null;
22216                 /**
22217                  * The element id
22218                  * @property id
22219                  */
22220                 this.id = this.el && el.id;
22221                 /**
22222                  * A reference to the style property
22223                  * @property css
22224                  */
22225                 this.css = this.el && el.style;
22226             },
22227
22228         /**
22229          * Returns the X position of an html element
22230          * @method getPosX
22231          * @param el the element for which to get the position
22232          * @return {int} the X coordinate
22233          * @for DragDropMgr
22234          * @deprecated use Roo.lib.Dom.getX instead
22235          * @static
22236          */
22237         getPosX: function(el) {
22238             return Roo.lib.Dom.getX(el);
22239         },
22240
22241         /**
22242          * Returns the Y position of an html element
22243          * @method getPosY
22244          * @param el the element for which to get the position
22245          * @return {int} the Y coordinate
22246          * @deprecated use Roo.lib.Dom.getY instead
22247          * @static
22248          */
22249         getPosY: function(el) {
22250             return Roo.lib.Dom.getY(el);
22251         },
22252
22253         /**
22254          * Swap two nodes.  In IE, we use the native method, for others we
22255          * emulate the IE behavior
22256          * @method swapNode
22257          * @param n1 the first node to swap
22258          * @param n2 the other node to swap
22259          * @static
22260          */
22261         swapNode: function(n1, n2) {
22262             if (n1.swapNode) {
22263                 n1.swapNode(n2);
22264             } else {
22265                 var p = n2.parentNode;
22266                 var s = n2.nextSibling;
22267
22268                 if (s == n1) {
22269                     p.insertBefore(n1, n2);
22270                 } else if (n2 == n1.nextSibling) {
22271                     p.insertBefore(n2, n1);
22272                 } else {
22273                     n1.parentNode.replaceChild(n2, n1);
22274                     p.insertBefore(n1, s);
22275                 }
22276             }
22277         },
22278
22279         /**
22280          * Returns the current scroll position
22281          * @method getScroll
22282          * @private
22283          * @static
22284          */
22285         getScroll: function () {
22286             var t, l, dde=document.documentElement, db=document.body;
22287             if (dde && (dde.scrollTop || dde.scrollLeft)) {
22288                 t = dde.scrollTop;
22289                 l = dde.scrollLeft;
22290             } else if (db) {
22291                 t = db.scrollTop;
22292                 l = db.scrollLeft;
22293             } else {
22294
22295             }
22296             return { top: t, left: l };
22297         },
22298
22299         /**
22300          * Returns the specified element style property
22301          * @method getStyle
22302          * @param {HTMLElement} el          the element
22303          * @param {string}      styleProp   the style property
22304          * @return {string} The value of the style property
22305          * @deprecated use Roo.lib.Dom.getStyle
22306          * @static
22307          */
22308         getStyle: function(el, styleProp) {
22309             return Roo.fly(el).getStyle(styleProp);
22310         },
22311
22312         /**
22313          * Gets the scrollTop
22314          * @method getScrollTop
22315          * @return {int} the document's scrollTop
22316          * @static
22317          */
22318         getScrollTop: function () { return this.getScroll().top; },
22319
22320         /**
22321          * Gets the scrollLeft
22322          * @method getScrollLeft
22323          * @return {int} the document's scrollTop
22324          * @static
22325          */
22326         getScrollLeft: function () { return this.getScroll().left; },
22327
22328         /**
22329          * Sets the x/y position of an element to the location of the
22330          * target element.
22331          * @method moveToEl
22332          * @param {HTMLElement} moveEl      The element to move
22333          * @param {HTMLElement} targetEl    The position reference element
22334          * @static
22335          */
22336         moveToEl: function (moveEl, targetEl) {
22337             var aCoord = Roo.lib.Dom.getXY(targetEl);
22338             Roo.lib.Dom.setXY(moveEl, aCoord);
22339         },
22340
22341         /**
22342          * Numeric array sort function
22343          * @method numericSort
22344          * @static
22345          */
22346         numericSort: function(a, b) { return (a - b); },
22347
22348         /**
22349          * Internal counter
22350          * @property _timeoutCount
22351          * @private
22352          * @static
22353          */
22354         _timeoutCount: 0,
22355
22356         /**
22357          * Trying to make the load order less important.  Without this we get
22358          * an error if this file is loaded before the Event Utility.
22359          * @method _addListeners
22360          * @private
22361          * @static
22362          */
22363         _addListeners: function() {
22364             var DDM = Roo.dd.DDM;
22365             if ( Roo.lib.Event && document ) {
22366                 DDM._onLoad();
22367             } else {
22368                 if (DDM._timeoutCount > 2000) {
22369                 } else {
22370                     setTimeout(DDM._addListeners, 10);
22371                     if (document && document.body) {
22372                         DDM._timeoutCount += 1;
22373                     }
22374                 }
22375             }
22376         },
22377
22378         /**
22379          * Recursively searches the immediate parent and all child nodes for
22380          * the handle element in order to determine wheter or not it was
22381          * clicked.
22382          * @method handleWasClicked
22383          * @param node the html element to inspect
22384          * @static
22385          */
22386         handleWasClicked: function(node, id) {
22387             if (this.isHandle(id, node.id)) {
22388                 return true;
22389             } else {
22390                 // check to see if this is a text node child of the one we want
22391                 var p = node.parentNode;
22392
22393                 while (p) {
22394                     if (this.isHandle(id, p.id)) {
22395                         return true;
22396                     } else {
22397                         p = p.parentNode;
22398                     }
22399                 }
22400             }
22401
22402             return false;
22403         }
22404
22405     };
22406
22407 }();
22408
22409 // shorter alias, save a few bytes
22410 Roo.dd.DDM = Roo.dd.DragDropMgr;
22411 Roo.dd.DDM._addListeners();
22412
22413 }/*
22414  * Based on:
22415  * Ext JS Library 1.1.1
22416  * Copyright(c) 2006-2007, Ext JS, LLC.
22417  *
22418  * Originally Released Under LGPL - original licence link has changed is not relivant.
22419  *
22420  * Fork - LGPL
22421  * <script type="text/javascript">
22422  */
22423
22424 /**
22425  * @class Roo.dd.DD
22426  * A DragDrop implementation where the linked element follows the
22427  * mouse cursor during a drag.
22428  * @extends Roo.dd.DragDrop
22429  * @constructor
22430  * @param {String} id the id of the linked element
22431  * @param {String} sGroup the group of related DragDrop items
22432  * @param {object} config an object containing configurable attributes
22433  *                Valid properties for DD:
22434  *                    scroll
22435  */
22436 Roo.dd.DD = function(id, sGroup, config) {
22437     if (id) {
22438         this.init(id, sGroup, config);
22439     }
22440 };
22441
22442 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22443
22444     /**
22445      * When set to true, the utility automatically tries to scroll the browser
22446      * window wehn a drag and drop element is dragged near the viewport boundary.
22447      * Defaults to true.
22448      * @property scroll
22449      * @type boolean
22450      */
22451     scroll: true,
22452
22453     /**
22454      * Sets the pointer offset to the distance between the linked element's top
22455      * left corner and the location the element was clicked
22456      * @method autoOffset
22457      * @param {int} iPageX the X coordinate of the click
22458      * @param {int} iPageY the Y coordinate of the click
22459      */
22460     autoOffset: function(iPageX, iPageY) {
22461         var x = iPageX - this.startPageX;
22462         var y = iPageY - this.startPageY;
22463         this.setDelta(x, y);
22464     },
22465
22466     /**
22467      * Sets the pointer offset.  You can call this directly to force the
22468      * offset to be in a particular location (e.g., pass in 0,0 to set it
22469      * to the center of the object)
22470      * @method setDelta
22471      * @param {int} iDeltaX the distance from the left
22472      * @param {int} iDeltaY the distance from the top
22473      */
22474     setDelta: function(iDeltaX, iDeltaY) {
22475         this.deltaX = iDeltaX;
22476         this.deltaY = iDeltaY;
22477     },
22478
22479     /**
22480      * Sets the drag element to the location of the mousedown or click event,
22481      * maintaining the cursor location relative to the location on the element
22482      * that was clicked.  Override this if you want to place the element in a
22483      * location other than where the cursor is.
22484      * @method setDragElPos
22485      * @param {int} iPageX the X coordinate of the mousedown or drag event
22486      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22487      */
22488     setDragElPos: function(iPageX, iPageY) {
22489         // the first time we do this, we are going to check to make sure
22490         // the element has css positioning
22491
22492         var el = this.getDragEl();
22493         this.alignElWithMouse(el, iPageX, iPageY);
22494     },
22495
22496     /**
22497      * Sets the element to the location of the mousedown or click event,
22498      * maintaining the cursor location relative to the location on the element
22499      * that was clicked.  Override this if you want to place the element in a
22500      * location other than where the cursor is.
22501      * @method alignElWithMouse
22502      * @param {HTMLElement} el the element to move
22503      * @param {int} iPageX the X coordinate of the mousedown or drag event
22504      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22505      */
22506     alignElWithMouse: function(el, iPageX, iPageY) {
22507         var oCoord = this.getTargetCoord(iPageX, iPageY);
22508         var fly = el.dom ? el : Roo.fly(el);
22509         if (!this.deltaSetXY) {
22510             var aCoord = [oCoord.x, oCoord.y];
22511             fly.setXY(aCoord);
22512             var newLeft = fly.getLeft(true);
22513             var newTop  = fly.getTop(true);
22514             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22515         } else {
22516             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22517         }
22518
22519         this.cachePosition(oCoord.x, oCoord.y);
22520         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22521         return oCoord;
22522     },
22523
22524     /**
22525      * Saves the most recent position so that we can reset the constraints and
22526      * tick marks on-demand.  We need to know this so that we can calculate the
22527      * number of pixels the element is offset from its original position.
22528      * @method cachePosition
22529      * @param iPageX the current x position (optional, this just makes it so we
22530      * don't have to look it up again)
22531      * @param iPageY the current y position (optional, this just makes it so we
22532      * don't have to look it up again)
22533      */
22534     cachePosition: function(iPageX, iPageY) {
22535         if (iPageX) {
22536             this.lastPageX = iPageX;
22537             this.lastPageY = iPageY;
22538         } else {
22539             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22540             this.lastPageX = aCoord[0];
22541             this.lastPageY = aCoord[1];
22542         }
22543     },
22544
22545     /**
22546      * Auto-scroll the window if the dragged object has been moved beyond the
22547      * visible window boundary.
22548      * @method autoScroll
22549      * @param {int} x the drag element's x position
22550      * @param {int} y the drag element's y position
22551      * @param {int} h the height of the drag element
22552      * @param {int} w the width of the drag element
22553      * @private
22554      */
22555     autoScroll: function(x, y, h, w) {
22556
22557         if (this.scroll) {
22558             // The client height
22559             var clientH = Roo.lib.Dom.getViewWidth();
22560
22561             // The client width
22562             var clientW = Roo.lib.Dom.getViewHeight();
22563
22564             // The amt scrolled down
22565             var st = this.DDM.getScrollTop();
22566
22567             // The amt scrolled right
22568             var sl = this.DDM.getScrollLeft();
22569
22570             // Location of the bottom of the element
22571             var bot = h + y;
22572
22573             // Location of the right of the element
22574             var right = w + x;
22575
22576             // The distance from the cursor to the bottom of the visible area,
22577             // adjusted so that we don't scroll if the cursor is beyond the
22578             // element drag constraints
22579             var toBot = (clientH + st - y - this.deltaY);
22580
22581             // The distance from the cursor to the right of the visible area
22582             var toRight = (clientW + sl - x - this.deltaX);
22583
22584
22585             // How close to the edge the cursor must be before we scroll
22586             // var thresh = (document.all) ? 100 : 40;
22587             var thresh = 40;
22588
22589             // How many pixels to scroll per autoscroll op.  This helps to reduce
22590             // clunky scrolling. IE is more sensitive about this ... it needs this
22591             // value to be higher.
22592             var scrAmt = (document.all) ? 80 : 30;
22593
22594             // Scroll down if we are near the bottom of the visible page and the
22595             // obj extends below the crease
22596             if ( bot > clientH && toBot < thresh ) {
22597                 window.scrollTo(sl, st + scrAmt);
22598             }
22599
22600             // Scroll up if the window is scrolled down and the top of the object
22601             // goes above the top border
22602             if ( y < st && st > 0 && y - st < thresh ) {
22603                 window.scrollTo(sl, st - scrAmt);
22604             }
22605
22606             // Scroll right if the obj is beyond the right border and the cursor is
22607             // near the border.
22608             if ( right > clientW && toRight < thresh ) {
22609                 window.scrollTo(sl + scrAmt, st);
22610             }
22611
22612             // Scroll left if the window has been scrolled to the right and the obj
22613             // extends past the left border
22614             if ( x < sl && sl > 0 && x - sl < thresh ) {
22615                 window.scrollTo(sl - scrAmt, st);
22616             }
22617         }
22618     },
22619
22620     /**
22621      * Finds the location the element should be placed if we want to move
22622      * it to where the mouse location less the click offset would place us.
22623      * @method getTargetCoord
22624      * @param {int} iPageX the X coordinate of the click
22625      * @param {int} iPageY the Y coordinate of the click
22626      * @return an object that contains the coordinates (Object.x and Object.y)
22627      * @private
22628      */
22629     getTargetCoord: function(iPageX, iPageY) {
22630
22631
22632         var x = iPageX - this.deltaX;
22633         var y = iPageY - this.deltaY;
22634
22635         if (this.constrainX) {
22636             if (x < this.minX) { x = this.minX; }
22637             if (x > this.maxX) { x = this.maxX; }
22638         }
22639
22640         if (this.constrainY) {
22641             if (y < this.minY) { y = this.minY; }
22642             if (y > this.maxY) { y = this.maxY; }
22643         }
22644
22645         x = this.getTick(x, this.xTicks);
22646         y = this.getTick(y, this.yTicks);
22647
22648
22649         return {x:x, y:y};
22650     },
22651
22652     /*
22653      * Sets up config options specific to this class. Overrides
22654      * Roo.dd.DragDrop, but all versions of this method through the
22655      * inheritance chain are called
22656      */
22657     applyConfig: function() {
22658         Roo.dd.DD.superclass.applyConfig.call(this);
22659         this.scroll = (this.config.scroll !== false);
22660     },
22661
22662     /*
22663      * Event that fires prior to the onMouseDown event.  Overrides
22664      * Roo.dd.DragDrop.
22665      */
22666     b4MouseDown: function(e) {
22667         // this.resetConstraints();
22668         this.autoOffset(e.getPageX(),
22669                             e.getPageY());
22670     },
22671
22672     /*
22673      * Event that fires prior to the onDrag event.  Overrides
22674      * Roo.dd.DragDrop.
22675      */
22676     b4Drag: function(e) {
22677         this.setDragElPos(e.getPageX(),
22678                             e.getPageY());
22679     },
22680
22681     toString: function() {
22682         return ("DD " + this.id);
22683     }
22684
22685     //////////////////////////////////////////////////////////////////////////
22686     // Debugging ygDragDrop events that can be overridden
22687     //////////////////////////////////////////////////////////////////////////
22688     /*
22689     startDrag: function(x, y) {
22690     },
22691
22692     onDrag: function(e) {
22693     },
22694
22695     onDragEnter: function(e, id) {
22696     },
22697
22698     onDragOver: function(e, id) {
22699     },
22700
22701     onDragOut: function(e, id) {
22702     },
22703
22704     onDragDrop: function(e, id) {
22705     },
22706
22707     endDrag: function(e) {
22708     }
22709
22710     */
22711
22712 });/*
22713  * Based on:
22714  * Ext JS Library 1.1.1
22715  * Copyright(c) 2006-2007, Ext JS, LLC.
22716  *
22717  * Originally Released Under LGPL - original licence link has changed is not relivant.
22718  *
22719  * Fork - LGPL
22720  * <script type="text/javascript">
22721  */
22722
22723 /**
22724  * @class Roo.dd.DDProxy
22725  * A DragDrop implementation that inserts an empty, bordered div into
22726  * the document that follows the cursor during drag operations.  At the time of
22727  * the click, the frame div is resized to the dimensions of the linked html
22728  * element, and moved to the exact location of the linked element.
22729  *
22730  * References to the "frame" element refer to the single proxy element that
22731  * was created to be dragged in place of all DDProxy elements on the
22732  * page.
22733  *
22734  * @extends Roo.dd.DD
22735  * @constructor
22736  * @param {String} id the id of the linked html element
22737  * @param {String} sGroup the group of related DragDrop objects
22738  * @param {object} config an object containing configurable attributes
22739  *                Valid properties for DDProxy in addition to those in DragDrop:
22740  *                   resizeFrame, centerFrame, dragElId
22741  */
22742 Roo.dd.DDProxy = function(id, sGroup, config) {
22743     if (id) {
22744         this.init(id, sGroup, config);
22745         this.initFrame();
22746     }
22747 };
22748
22749 /**
22750  * The default drag frame div id
22751  * @property Roo.dd.DDProxy.dragElId
22752  * @type String
22753  * @static
22754  */
22755 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22756
22757 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22758
22759     /**
22760      * By default we resize the drag frame to be the same size as the element
22761      * we want to drag (this is to get the frame effect).  We can turn it off
22762      * if we want a different behavior.
22763      * @property resizeFrame
22764      * @type boolean
22765      */
22766     resizeFrame: true,
22767
22768     /**
22769      * By default the frame is positioned exactly where the drag element is, so
22770      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22771      * you do not have constraints on the obj is to have the drag frame centered
22772      * around the cursor.  Set centerFrame to true for this effect.
22773      * @property centerFrame
22774      * @type boolean
22775      */
22776     centerFrame: false,
22777
22778     /**
22779      * Creates the proxy element if it does not yet exist
22780      * @method createFrame
22781      */
22782     createFrame: function() {
22783         var self = this;
22784         var body = document.body;
22785
22786         if (!body || !body.firstChild) {
22787             setTimeout( function() { self.createFrame(); }, 50 );
22788             return;
22789         }
22790
22791         var div = this.getDragEl();
22792
22793         if (!div) {
22794             div    = document.createElement("div");
22795             div.id = this.dragElId;
22796             var s  = div.style;
22797
22798             s.position   = "absolute";
22799             s.visibility = "hidden";
22800             s.cursor     = "move";
22801             s.border     = "2px solid #aaa";
22802             s.zIndex     = 999;
22803
22804             // appendChild can blow up IE if invoked prior to the window load event
22805             // while rendering a table.  It is possible there are other scenarios
22806             // that would cause this to happen as well.
22807             body.insertBefore(div, body.firstChild);
22808         }
22809     },
22810
22811     /**
22812      * Initialization for the drag frame element.  Must be called in the
22813      * constructor of all subclasses
22814      * @method initFrame
22815      */
22816     initFrame: function() {
22817         this.createFrame();
22818     },
22819
22820     applyConfig: function() {
22821         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22822
22823         this.resizeFrame = (this.config.resizeFrame !== false);
22824         this.centerFrame = (this.config.centerFrame);
22825         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22826     },
22827
22828     /**
22829      * Resizes the drag frame to the dimensions of the clicked object, positions
22830      * it over the object, and finally displays it
22831      * @method showFrame
22832      * @param {int} iPageX X click position
22833      * @param {int} iPageY Y click position
22834      * @private
22835      */
22836     showFrame: function(iPageX, iPageY) {
22837         var el = this.getEl();
22838         var dragEl = this.getDragEl();
22839         var s = dragEl.style;
22840
22841         this._resizeProxy();
22842
22843         if (this.centerFrame) {
22844             this.setDelta( Math.round(parseInt(s.width,  10)/2),
22845                            Math.round(parseInt(s.height, 10)/2) );
22846         }
22847
22848         this.setDragElPos(iPageX, iPageY);
22849
22850         Roo.fly(dragEl).show();
22851     },
22852
22853     /**
22854      * The proxy is automatically resized to the dimensions of the linked
22855      * element when a drag is initiated, unless resizeFrame is set to false
22856      * @method _resizeProxy
22857      * @private
22858      */
22859     _resizeProxy: function() {
22860         if (this.resizeFrame) {
22861             var el = this.getEl();
22862             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22863         }
22864     },
22865
22866     // overrides Roo.dd.DragDrop
22867     b4MouseDown: function(e) {
22868         var x = e.getPageX();
22869         var y = e.getPageY();
22870         this.autoOffset(x, y);
22871         this.setDragElPos(x, y);
22872     },
22873
22874     // overrides Roo.dd.DragDrop
22875     b4StartDrag: function(x, y) {
22876         // show the drag frame
22877         this.showFrame(x, y);
22878     },
22879
22880     // overrides Roo.dd.DragDrop
22881     b4EndDrag: function(e) {
22882         Roo.fly(this.getDragEl()).hide();
22883     },
22884
22885     // overrides Roo.dd.DragDrop
22886     // By default we try to move the element to the last location of the frame.
22887     // This is so that the default behavior mirrors that of Roo.dd.DD.
22888     endDrag: function(e) {
22889
22890         var lel = this.getEl();
22891         var del = this.getDragEl();
22892
22893         // Show the drag frame briefly so we can get its position
22894         del.style.visibility = "";
22895
22896         this.beforeMove();
22897         // Hide the linked element before the move to get around a Safari
22898         // rendering bug.
22899         lel.style.visibility = "hidden";
22900         Roo.dd.DDM.moveToEl(lel, del);
22901         del.style.visibility = "hidden";
22902         lel.style.visibility = "";
22903
22904         this.afterDrag();
22905     },
22906
22907     beforeMove : function(){
22908
22909     },
22910
22911     afterDrag : function(){
22912
22913     },
22914
22915     toString: function() {
22916         return ("DDProxy " + this.id);
22917     }
22918
22919 });
22920 /*
22921  * Based on:
22922  * Ext JS Library 1.1.1
22923  * Copyright(c) 2006-2007, Ext JS, LLC.
22924  *
22925  * Originally Released Under LGPL - original licence link has changed is not relivant.
22926  *
22927  * Fork - LGPL
22928  * <script type="text/javascript">
22929  */
22930
22931  /**
22932  * @class Roo.dd.DDTarget
22933  * A DragDrop implementation that does not move, but can be a drop
22934  * target.  You would get the same result by simply omitting implementation
22935  * for the event callbacks, but this way we reduce the processing cost of the
22936  * event listener and the callbacks.
22937  * @extends Roo.dd.DragDrop
22938  * @constructor
22939  * @param {String} id the id of the element that is a drop target
22940  * @param {String} sGroup the group of related DragDrop objects
22941  * @param {object} config an object containing configurable attributes
22942  *                 Valid properties for DDTarget in addition to those in
22943  *                 DragDrop:
22944  *                    none
22945  */
22946 Roo.dd.DDTarget = function(id, sGroup, config) {
22947     if (id) {
22948         this.initTarget(id, sGroup, config);
22949     }
22950     if (config && (config.listeners || config.events)) { 
22951         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
22952             listeners : config.listeners || {}, 
22953             events : config.events || {} 
22954         });    
22955     }
22956 };
22957
22958 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22959 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22960     toString: function() {
22961         return ("DDTarget " + this.id);
22962     }
22963 });
22964 /*
22965  * Based on:
22966  * Ext JS Library 1.1.1
22967  * Copyright(c) 2006-2007, Ext JS, LLC.
22968  *
22969  * Originally Released Under LGPL - original licence link has changed is not relivant.
22970  *
22971  * Fork - LGPL
22972  * <script type="text/javascript">
22973  */
22974  
22975
22976 /**
22977  * @class Roo.dd.ScrollManager
22978  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22979  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22980  * @static
22981  */
22982 Roo.dd.ScrollManager = function(){
22983     var ddm = Roo.dd.DragDropMgr;
22984     var els = {};
22985     var dragEl = null;
22986     var proc = {};
22987     
22988     
22989     
22990     var onStop = function(e){
22991         dragEl = null;
22992         clearProc();
22993     };
22994     
22995     var triggerRefresh = function(){
22996         if(ddm.dragCurrent){
22997              ddm.refreshCache(ddm.dragCurrent.groups);
22998         }
22999     };
23000     
23001     var doScroll = function(){
23002         if(ddm.dragCurrent){
23003             var dds = Roo.dd.ScrollManager;
23004             if(!dds.animate){
23005                 if(proc.el.scroll(proc.dir, dds.increment)){
23006                     triggerRefresh();
23007                 }
23008             }else{
23009                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
23010             }
23011         }
23012     };
23013     
23014     var clearProc = function(){
23015         if(proc.id){
23016             clearInterval(proc.id);
23017         }
23018         proc.id = 0;
23019         proc.el = null;
23020         proc.dir = "";
23021     };
23022     
23023     var startProc = function(el, dir){
23024          Roo.log('scroll startproc');
23025         clearProc();
23026         proc.el = el;
23027         proc.dir = dir;
23028         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
23029     };
23030     
23031     var onFire = function(e, isDrop){
23032        
23033         if(isDrop || !ddm.dragCurrent){ return; }
23034         var dds = Roo.dd.ScrollManager;
23035         if(!dragEl || dragEl != ddm.dragCurrent){
23036             dragEl = ddm.dragCurrent;
23037             // refresh regions on drag start
23038             dds.refreshCache();
23039         }
23040         
23041         var xy = Roo.lib.Event.getXY(e);
23042         var pt = new Roo.lib.Point(xy[0], xy[1]);
23043         for(var id in els){
23044             var el = els[id], r = el._region;
23045             if(r && r.contains(pt) && el.isScrollable()){
23046                 if(r.bottom - pt.y <= dds.thresh){
23047                     if(proc.el != el){
23048                         startProc(el, "down");
23049                     }
23050                     return;
23051                 }else if(r.right - pt.x <= dds.thresh){
23052                     if(proc.el != el){
23053                         startProc(el, "left");
23054                     }
23055                     return;
23056                 }else if(pt.y - r.top <= dds.thresh){
23057                     if(proc.el != el){
23058                         startProc(el, "up");
23059                     }
23060                     return;
23061                 }else if(pt.x - r.left <= dds.thresh){
23062                     if(proc.el != el){
23063                         startProc(el, "right");
23064                     }
23065                     return;
23066                 }
23067             }
23068         }
23069         clearProc();
23070     };
23071     
23072     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
23073     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
23074     
23075     return {
23076         /**
23077          * Registers new overflow element(s) to auto scroll
23078          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
23079          */
23080         register : function(el){
23081             if(el instanceof Array){
23082                 for(var i = 0, len = el.length; i < len; i++) {
23083                         this.register(el[i]);
23084                 }
23085             }else{
23086                 el = Roo.get(el);
23087                 els[el.id] = el;
23088             }
23089             Roo.dd.ScrollManager.els = els;
23090         },
23091         
23092         /**
23093          * Unregisters overflow element(s) so they are no longer scrolled
23094          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
23095          */
23096         unregister : function(el){
23097             if(el instanceof Array){
23098                 for(var i = 0, len = el.length; i < len; i++) {
23099                         this.unregister(el[i]);
23100                 }
23101             }else{
23102                 el = Roo.get(el);
23103                 delete els[el.id];
23104             }
23105         },
23106         
23107         /**
23108          * The number of pixels from the edge of a container the pointer needs to be to 
23109          * trigger scrolling (defaults to 25)
23110          * @type Number
23111          */
23112         thresh : 25,
23113         
23114         /**
23115          * The number of pixels to scroll in each scroll increment (defaults to 50)
23116          * @type Number
23117          */
23118         increment : 100,
23119         
23120         /**
23121          * The frequency of scrolls in milliseconds (defaults to 500)
23122          * @type Number
23123          */
23124         frequency : 500,
23125         
23126         /**
23127          * True to animate the scroll (defaults to true)
23128          * @type Boolean
23129          */
23130         animate: true,
23131         
23132         /**
23133          * The animation duration in seconds - 
23134          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
23135          * @type Number
23136          */
23137         animDuration: .4,
23138         
23139         /**
23140          * Manually trigger a cache refresh.
23141          */
23142         refreshCache : function(){
23143             for(var id in els){
23144                 if(typeof els[id] == 'object'){ // for people extending the object prototype
23145                     els[id]._region = els[id].getRegion();
23146                 }
23147             }
23148         }
23149     };
23150 }();/*
23151  * Based on:
23152  * Ext JS Library 1.1.1
23153  * Copyright(c) 2006-2007, Ext JS, LLC.
23154  *
23155  * Originally Released Under LGPL - original licence link has changed is not relivant.
23156  *
23157  * Fork - LGPL
23158  * <script type="text/javascript">
23159  */
23160  
23161
23162 /**
23163  * @class Roo.dd.Registry
23164  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
23165  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
23166  * @static
23167  */
23168 Roo.dd.Registry = function(){
23169     var elements = {}; 
23170     var handles = {}; 
23171     var autoIdSeed = 0;
23172
23173     var getId = function(el, autogen){
23174         if(typeof el == "string"){
23175             return el;
23176         }
23177         var id = el.id;
23178         if(!id && autogen !== false){
23179             id = "roodd-" + (++autoIdSeed);
23180             el.id = id;
23181         }
23182         return id;
23183     };
23184     
23185     return {
23186     /**
23187      * Register a drag drop element
23188      * @param {String|HTMLElement} element The id or DOM node to register
23189      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
23190      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
23191      * knows how to interpret, plus there are some specific properties known to the Registry that should be
23192      * populated in the data object (if applicable):
23193      * <pre>
23194 Value      Description<br />
23195 ---------  ------------------------------------------<br />
23196 handles    Array of DOM nodes that trigger dragging<br />
23197            for the element being registered<br />
23198 isHandle   True if the element passed in triggers<br />
23199            dragging itself, else false
23200 </pre>
23201      */
23202         register : function(el, data){
23203             data = data || {};
23204             if(typeof el == "string"){
23205                 el = document.getElementById(el);
23206             }
23207             data.ddel = el;
23208             elements[getId(el)] = data;
23209             if(data.isHandle !== false){
23210                 handles[data.ddel.id] = data;
23211             }
23212             if(data.handles){
23213                 var hs = data.handles;
23214                 for(var i = 0, len = hs.length; i < len; i++){
23215                         handles[getId(hs[i])] = data;
23216                 }
23217             }
23218         },
23219
23220     /**
23221      * Unregister a drag drop element
23222      * @param {String|HTMLElement}  element The id or DOM node to unregister
23223      */
23224         unregister : function(el){
23225             var id = getId(el, false);
23226             var data = elements[id];
23227             if(data){
23228                 delete elements[id];
23229                 if(data.handles){
23230                     var hs = data.handles;
23231                     for(var i = 0, len = hs.length; i < len; i++){
23232                         delete handles[getId(hs[i], false)];
23233                     }
23234                 }
23235             }
23236         },
23237
23238     /**
23239      * Returns the handle registered for a DOM Node by id
23240      * @param {String|HTMLElement} id The DOM node or id to look up
23241      * @return {Object} handle The custom handle data
23242      */
23243         getHandle : function(id){
23244             if(typeof id != "string"){ // must be element?
23245                 id = id.id;
23246             }
23247             return handles[id];
23248         },
23249
23250     /**
23251      * Returns the handle that is registered for the DOM node that is the target of the event
23252      * @param {Event} e The event
23253      * @return {Object} handle The custom handle data
23254      */
23255         getHandleFromEvent : function(e){
23256             var t = Roo.lib.Event.getTarget(e);
23257             return t ? handles[t.id] : null;
23258         },
23259
23260     /**
23261      * Returns a custom data object that is registered for a DOM node by id
23262      * @param {String|HTMLElement} id The DOM node or id to look up
23263      * @return {Object} data The custom data
23264      */
23265         getTarget : function(id){
23266             if(typeof id != "string"){ // must be element?
23267                 id = id.id;
23268             }
23269             return elements[id];
23270         },
23271
23272     /**
23273      * Returns a custom data object that is registered for the DOM node that is the target of the event
23274      * @param {Event} e The event
23275      * @return {Object} data The custom data
23276      */
23277         getTargetFromEvent : function(e){
23278             var t = Roo.lib.Event.getTarget(e);
23279             return t ? elements[t.id] || handles[t.id] : null;
23280         }
23281     };
23282 }();/*
23283  * Based on:
23284  * Ext JS Library 1.1.1
23285  * Copyright(c) 2006-2007, Ext JS, LLC.
23286  *
23287  * Originally Released Under LGPL - original licence link has changed is not relivant.
23288  *
23289  * Fork - LGPL
23290  * <script type="text/javascript">
23291  */
23292  
23293
23294 /**
23295  * @class Roo.dd.StatusProxy
23296  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
23297  * default drag proxy used by all Roo.dd components.
23298  * @constructor
23299  * @param {Object} config
23300  */
23301 Roo.dd.StatusProxy = function(config){
23302     Roo.apply(this, config);
23303     this.id = this.id || Roo.id();
23304     this.el = new Roo.Layer({
23305         dh: {
23306             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23307                 {tag: "div", cls: "x-dd-drop-icon"},
23308                 {tag: "div", cls: "x-dd-drag-ghost"}
23309             ]
23310         }, 
23311         shadow: !config || config.shadow !== false
23312     });
23313     this.ghost = Roo.get(this.el.dom.childNodes[1]);
23314     this.dropStatus = this.dropNotAllowed;
23315 };
23316
23317 Roo.dd.StatusProxy.prototype = {
23318     /**
23319      * @cfg {String} dropAllowed
23320      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23321      */
23322     dropAllowed : "x-dd-drop-ok",
23323     /**
23324      * @cfg {String} dropNotAllowed
23325      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23326      */
23327     dropNotAllowed : "x-dd-drop-nodrop",
23328
23329     /**
23330      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23331      * over the current target element.
23332      * @param {String} cssClass The css class for the new drop status indicator image
23333      */
23334     setStatus : function(cssClass){
23335         cssClass = cssClass || this.dropNotAllowed;
23336         if(this.dropStatus != cssClass){
23337             this.el.replaceClass(this.dropStatus, cssClass);
23338             this.dropStatus = cssClass;
23339         }
23340     },
23341
23342     /**
23343      * Resets the status indicator to the default dropNotAllowed value
23344      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23345      */
23346     reset : function(clearGhost){
23347         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23348         this.dropStatus = this.dropNotAllowed;
23349         if(clearGhost){
23350             this.ghost.update("");
23351         }
23352     },
23353
23354     /**
23355      * Updates the contents of the ghost element
23356      * @param {String} html The html that will replace the current innerHTML of the ghost element
23357      */
23358     update : function(html){
23359         if(typeof html == "string"){
23360             this.ghost.update(html);
23361         }else{
23362             this.ghost.update("");
23363             html.style.margin = "0";
23364             this.ghost.dom.appendChild(html);
23365         }
23366         // ensure float = none set?? cant remember why though.
23367         var el = this.ghost.dom.firstChild;
23368                 if(el){
23369                         Roo.fly(el).setStyle('float', 'none');
23370                 }
23371     },
23372     
23373     /**
23374      * Returns the underlying proxy {@link Roo.Layer}
23375      * @return {Roo.Layer} el
23376     */
23377     getEl : function(){
23378         return this.el;
23379     },
23380
23381     /**
23382      * Returns the ghost element
23383      * @return {Roo.Element} el
23384      */
23385     getGhost : function(){
23386         return this.ghost;
23387     },
23388
23389     /**
23390      * Hides the proxy
23391      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23392      */
23393     hide : function(clear){
23394         this.el.hide();
23395         if(clear){
23396             this.reset(true);
23397         }
23398     },
23399
23400     /**
23401      * Stops the repair animation if it's currently running
23402      */
23403     stop : function(){
23404         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23405             this.anim.stop();
23406         }
23407     },
23408
23409     /**
23410      * Displays this proxy
23411      */
23412     show : function(){
23413         this.el.show();
23414     },
23415
23416     /**
23417      * Force the Layer to sync its shadow and shim positions to the element
23418      */
23419     sync : function(){
23420         this.el.sync();
23421     },
23422
23423     /**
23424      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23425      * invalid drop operation by the item being dragged.
23426      * @param {Array} xy The XY position of the element ([x, y])
23427      * @param {Function} callback The function to call after the repair is complete
23428      * @param {Object} scope The scope in which to execute the callback
23429      */
23430     repair : function(xy, callback, scope){
23431         this.callback = callback;
23432         this.scope = scope;
23433         if(xy && this.animRepair !== false){
23434             this.el.addClass("x-dd-drag-repair");
23435             this.el.hideUnders(true);
23436             this.anim = this.el.shift({
23437                 duration: this.repairDuration || .5,
23438                 easing: 'easeOut',
23439                 xy: xy,
23440                 stopFx: true,
23441                 callback: this.afterRepair,
23442                 scope: this
23443             });
23444         }else{
23445             this.afterRepair();
23446         }
23447     },
23448
23449     // private
23450     afterRepair : function(){
23451         this.hide(true);
23452         if(typeof this.callback == "function"){
23453             this.callback.call(this.scope || this);
23454         }
23455         this.callback = null;
23456         this.scope = null;
23457     }
23458 };/*
23459  * Based on:
23460  * Ext JS Library 1.1.1
23461  * Copyright(c) 2006-2007, Ext JS, LLC.
23462  *
23463  * Originally Released Under LGPL - original licence link has changed is not relivant.
23464  *
23465  * Fork - LGPL
23466  * <script type="text/javascript">
23467  */
23468
23469 /**
23470  * @class Roo.dd.DragSource
23471  * @extends Roo.dd.DDProxy
23472  * A simple class that provides the basic implementation needed to make any element draggable.
23473  * @constructor
23474  * @param {String/HTMLElement/Element} el The container element
23475  * @param {Object} config
23476  */
23477 Roo.dd.DragSource = function(el, config){
23478     this.el = Roo.get(el);
23479     this.dragData = {};
23480     
23481     Roo.apply(this, config);
23482     
23483     if(!this.proxy){
23484         this.proxy = new Roo.dd.StatusProxy();
23485     }
23486
23487     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23488           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23489     
23490     this.dragging = false;
23491 };
23492
23493 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23494     /**
23495      * @cfg {String} dropAllowed
23496      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23497      */
23498     dropAllowed : "x-dd-drop-ok",
23499     /**
23500      * @cfg {String} dropNotAllowed
23501      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23502      */
23503     dropNotAllowed : "x-dd-drop-nodrop",
23504
23505     /**
23506      * Returns the data object associated with this drag source
23507      * @return {Object} data An object containing arbitrary data
23508      */
23509     getDragData : function(e){
23510         return this.dragData;
23511     },
23512
23513     // private
23514     onDragEnter : function(e, id){
23515         var target = Roo.dd.DragDropMgr.getDDById(id);
23516         this.cachedTarget = target;
23517         if(this.beforeDragEnter(target, e, id) !== false){
23518             if(target.isNotifyTarget){
23519                 var status = target.notifyEnter(this, e, this.dragData);
23520                 this.proxy.setStatus(status);
23521             }else{
23522                 this.proxy.setStatus(this.dropAllowed);
23523             }
23524             
23525             if(this.afterDragEnter){
23526                 /**
23527                  * An empty function by default, but provided so that you can perform a custom action
23528                  * when the dragged item enters the drop target by providing an implementation.
23529                  * @param {Roo.dd.DragDrop} target The drop target
23530                  * @param {Event} e The event object
23531                  * @param {String} id The id of the dragged element
23532                  * @method afterDragEnter
23533                  */
23534                 this.afterDragEnter(target, e, id);
23535             }
23536         }
23537     },
23538
23539     /**
23540      * An empty function by default, but provided so that you can perform a custom action
23541      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23542      * @param {Roo.dd.DragDrop} target The drop target
23543      * @param {Event} e The event object
23544      * @param {String} id The id of the dragged element
23545      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23546      */
23547     beforeDragEnter : function(target, e, id){
23548         return true;
23549     },
23550
23551     // private
23552     alignElWithMouse: function() {
23553         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23554         this.proxy.sync();
23555     },
23556
23557     // private
23558     onDragOver : function(e, id){
23559         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23560         if(this.beforeDragOver(target, e, id) !== false){
23561             if(target.isNotifyTarget){
23562                 var status = target.notifyOver(this, e, this.dragData);
23563                 this.proxy.setStatus(status);
23564             }
23565
23566             if(this.afterDragOver){
23567                 /**
23568                  * An empty function by default, but provided so that you can perform a custom action
23569                  * while the dragged item is over the drop target by providing an implementation.
23570                  * @param {Roo.dd.DragDrop} target The drop target
23571                  * @param {Event} e The event object
23572                  * @param {String} id The id of the dragged element
23573                  * @method afterDragOver
23574                  */
23575                 this.afterDragOver(target, e, id);
23576             }
23577         }
23578     },
23579
23580     /**
23581      * An empty function by default, but provided so that you can perform a custom action
23582      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23583      * @param {Roo.dd.DragDrop} target The drop target
23584      * @param {Event} e The event object
23585      * @param {String} id The id of the dragged element
23586      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23587      */
23588     beforeDragOver : function(target, e, id){
23589         return true;
23590     },
23591
23592     // private
23593     onDragOut : function(e, id){
23594         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23595         if(this.beforeDragOut(target, e, id) !== false){
23596             if(target.isNotifyTarget){
23597                 target.notifyOut(this, e, this.dragData);
23598             }
23599             this.proxy.reset();
23600             if(this.afterDragOut){
23601                 /**
23602                  * An empty function by default, but provided so that you can perform a custom action
23603                  * after the dragged item is dragged out of the target without dropping.
23604                  * @param {Roo.dd.DragDrop} target The drop target
23605                  * @param {Event} e The event object
23606                  * @param {String} id The id of the dragged element
23607                  * @method afterDragOut
23608                  */
23609                 this.afterDragOut(target, e, id);
23610             }
23611         }
23612         this.cachedTarget = null;
23613     },
23614
23615     /**
23616      * An empty function by default, but provided so that you can perform a custom action before the dragged
23617      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23618      * @param {Roo.dd.DragDrop} target The drop target
23619      * @param {Event} e The event object
23620      * @param {String} id The id of the dragged element
23621      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23622      */
23623     beforeDragOut : function(target, e, id){
23624         return true;
23625     },
23626     
23627     // private
23628     onDragDrop : function(e, id){
23629         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23630         if(this.beforeDragDrop(target, e, id) !== false){
23631             if(target.isNotifyTarget){
23632                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23633                     this.onValidDrop(target, e, id);
23634                 }else{
23635                     this.onInvalidDrop(target, e, id);
23636                 }
23637             }else{
23638                 this.onValidDrop(target, e, id);
23639             }
23640             
23641             if(this.afterDragDrop){
23642                 /**
23643                  * An empty function by default, but provided so that you can perform a custom action
23644                  * after a valid drag drop has occurred by providing an implementation.
23645                  * @param {Roo.dd.DragDrop} target The drop target
23646                  * @param {Event} e The event object
23647                  * @param {String} id The id of the dropped element
23648                  * @method afterDragDrop
23649                  */
23650                 this.afterDragDrop(target, e, id);
23651             }
23652         }
23653         delete this.cachedTarget;
23654     },
23655
23656     /**
23657      * An empty function by default, but provided so that you can perform a custom action before the dragged
23658      * item is dropped onto the target and optionally cancel the onDragDrop.
23659      * @param {Roo.dd.DragDrop} target The drop target
23660      * @param {Event} e The event object
23661      * @param {String} id The id of the dragged element
23662      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23663      */
23664     beforeDragDrop : function(target, e, id){
23665         return true;
23666     },
23667
23668     // private
23669     onValidDrop : function(target, e, id){
23670         this.hideProxy();
23671         if(this.afterValidDrop){
23672             /**
23673              * An empty function by default, but provided so that you can perform a custom action
23674              * after a valid drop has occurred by providing an implementation.
23675              * @param {Object} target The target DD 
23676              * @param {Event} e The event object
23677              * @param {String} id The id of the dropped element
23678              * @method afterInvalidDrop
23679              */
23680             this.afterValidDrop(target, e, id);
23681         }
23682     },
23683
23684     // private
23685     getRepairXY : function(e, data){
23686         return this.el.getXY();  
23687     },
23688
23689     // private
23690     onInvalidDrop : function(target, e, id){
23691         this.beforeInvalidDrop(target, e, id);
23692         if(this.cachedTarget){
23693             if(this.cachedTarget.isNotifyTarget){
23694                 this.cachedTarget.notifyOut(this, e, this.dragData);
23695             }
23696             this.cacheTarget = null;
23697         }
23698         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23699
23700         if(this.afterInvalidDrop){
23701             /**
23702              * An empty function by default, but provided so that you can perform a custom action
23703              * after an invalid drop has occurred by providing an implementation.
23704              * @param {Event} e The event object
23705              * @param {String} id The id of the dropped element
23706              * @method afterInvalidDrop
23707              */
23708             this.afterInvalidDrop(e, id);
23709         }
23710     },
23711
23712     // private
23713     afterRepair : function(){
23714         if(Roo.enableFx){
23715             this.el.highlight(this.hlColor || "c3daf9");
23716         }
23717         this.dragging = false;
23718     },
23719
23720     /**
23721      * An empty function by default, but provided so that you can perform a custom action after an invalid
23722      * drop has occurred.
23723      * @param {Roo.dd.DragDrop} target The drop target
23724      * @param {Event} e The event object
23725      * @param {String} id The id of the dragged element
23726      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23727      */
23728     beforeInvalidDrop : function(target, e, id){
23729         return true;
23730     },
23731
23732     // private
23733     handleMouseDown : function(e){
23734         if(this.dragging) {
23735             return;
23736         }
23737         var data = this.getDragData(e);
23738         if(data && this.onBeforeDrag(data, e) !== false){
23739             this.dragData = data;
23740             this.proxy.stop();
23741             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23742         } 
23743     },
23744
23745     /**
23746      * An empty function by default, but provided so that you can perform a custom action before the initial
23747      * drag event begins and optionally cancel it.
23748      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23749      * @param {Event} e The event object
23750      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23751      */
23752     onBeforeDrag : function(data, e){
23753         return true;
23754     },
23755
23756     /**
23757      * An empty function by default, but provided so that you can perform a custom action once the initial
23758      * drag event has begun.  The drag cannot be canceled from this function.
23759      * @param {Number} x The x position of the click on the dragged object
23760      * @param {Number} y The y position of the click on the dragged object
23761      */
23762     onStartDrag : Roo.emptyFn,
23763
23764     // private - YUI override
23765     startDrag : function(x, y){
23766         this.proxy.reset();
23767         this.dragging = true;
23768         this.proxy.update("");
23769         this.onInitDrag(x, y);
23770         this.proxy.show();
23771     },
23772
23773     // private
23774     onInitDrag : function(x, y){
23775         var clone = this.el.dom.cloneNode(true);
23776         clone.id = Roo.id(); // prevent duplicate ids
23777         this.proxy.update(clone);
23778         this.onStartDrag(x, y);
23779         return true;
23780     },
23781
23782     /**
23783      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23784      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23785      */
23786     getProxy : function(){
23787         return this.proxy;  
23788     },
23789
23790     /**
23791      * Hides the drag source's {@link Roo.dd.StatusProxy}
23792      */
23793     hideProxy : function(){
23794         this.proxy.hide();  
23795         this.proxy.reset(true);
23796         this.dragging = false;
23797     },
23798
23799     // private
23800     triggerCacheRefresh : function(){
23801         Roo.dd.DDM.refreshCache(this.groups);
23802     },
23803
23804     // private - override to prevent hiding
23805     b4EndDrag: function(e) {
23806     },
23807
23808     // private - override to prevent moving
23809     endDrag : function(e){
23810         this.onEndDrag(this.dragData, e);
23811     },
23812
23813     // private
23814     onEndDrag : function(data, e){
23815     },
23816     
23817     // private - pin to cursor
23818     autoOffset : function(x, y) {
23819         this.setDelta(-12, -20);
23820     }    
23821 });/*
23822  * Based on:
23823  * Ext JS Library 1.1.1
23824  * Copyright(c) 2006-2007, Ext JS, LLC.
23825  *
23826  * Originally Released Under LGPL - original licence link has changed is not relivant.
23827  *
23828  * Fork - LGPL
23829  * <script type="text/javascript">
23830  */
23831
23832
23833 /**
23834  * @class Roo.dd.DropTarget
23835  * @extends Roo.dd.DDTarget
23836  * A simple class that provides the basic implementation needed to make any element a drop target that can have
23837  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
23838  * @constructor
23839  * @param {String/HTMLElement/Element} el The container element
23840  * @param {Object} config
23841  */
23842 Roo.dd.DropTarget = function(el, config){
23843     this.el = Roo.get(el);
23844     
23845     var listeners = false; ;
23846     if (config && config.listeners) {
23847         listeners= config.listeners;
23848         delete config.listeners;
23849     }
23850     Roo.apply(this, config);
23851     
23852     if(this.containerScroll){
23853         Roo.dd.ScrollManager.register(this.el);
23854     }
23855     this.addEvents( {
23856          /**
23857          * @scope Roo.dd.DropTarget
23858          */
23859          
23860          /**
23861          * @event enter
23862          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23863          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
23864          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
23865          * 
23866          * IMPORTANT : it should set  this.valid to true|false
23867          * 
23868          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23869          * @param {Event} e The event
23870          * @param {Object} data An object containing arbitrary data supplied by the drag source
23871          */
23872         "enter" : true,
23873         
23874          /**
23875          * @event over
23876          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23877          * This method will be called on every mouse movement while the drag source is over the drop target.
23878          * This default implementation simply returns the dropAllowed config value.
23879          * 
23880          * IMPORTANT : it should set  this.valid to true|false
23881          * 
23882          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23883          * @param {Event} e The event
23884          * @param {Object} data An object containing arbitrary data supplied by the drag source
23885          
23886          */
23887         "over" : true,
23888         /**
23889          * @event out
23890          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23891          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
23892          * overClass (if any) from the drop element.
23893          * 
23894          * 
23895          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23896          * @param {Event} e The event
23897          * @param {Object} data An object containing arbitrary data supplied by the drag source
23898          */
23899          "out" : true,
23900          
23901         /**
23902          * @event drop
23903          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23904          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
23905          * implementation that does something to process the drop event and returns true so that the drag source's
23906          * repair action does not run.
23907          * 
23908          * IMPORTANT : it should set this.success
23909          * 
23910          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23911          * @param {Event} e The event
23912          * @param {Object} data An object containing arbitrary data supplied by the drag source
23913         */
23914          "drop" : true
23915     });
23916             
23917      
23918     Roo.dd.DropTarget.superclass.constructor.call(  this, 
23919         this.el.dom, 
23920         this.ddGroup || this.group,
23921         {
23922             isTarget: true,
23923             listeners : listeners || {} 
23924            
23925         
23926         }
23927     );
23928
23929 };
23930
23931 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23932     /**
23933      * @cfg {String} overClass
23934      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23935      */
23936      /**
23937      * @cfg {String} ddGroup
23938      * The drag drop group to handle drop events for
23939      */
23940      
23941     /**
23942      * @cfg {String} dropAllowed
23943      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23944      */
23945     dropAllowed : "x-dd-drop-ok",
23946     /**
23947      * @cfg {String} dropNotAllowed
23948      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23949      */
23950     dropNotAllowed : "x-dd-drop-nodrop",
23951     /**
23952      * @cfg {boolean} success
23953      * set this after drop listener.. 
23954      */
23955     success : false,
23956     /**
23957      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23958      * if the drop point is valid for over/enter..
23959      */
23960     valid : false,
23961     // private
23962     isTarget : true,
23963
23964     // private
23965     isNotifyTarget : true,
23966     
23967     /**
23968      * @hide
23969      */
23970     notifyEnter : function(dd, e, data)
23971     {
23972         this.valid = true;
23973         this.fireEvent('enter', dd, e, data);
23974         if(this.overClass){
23975             this.el.addClass(this.overClass);
23976         }
23977         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23978             this.valid ? this.dropAllowed : this.dropNotAllowed
23979         );
23980     },
23981
23982     /**
23983      * @hide
23984      */
23985     notifyOver : function(dd, e, data)
23986     {
23987         this.valid = true;
23988         this.fireEvent('over', dd, e, data);
23989         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23990             this.valid ? this.dropAllowed : this.dropNotAllowed
23991         );
23992     },
23993
23994     /**
23995      * @hide
23996      */
23997     notifyOut : function(dd, e, data)
23998     {
23999         this.fireEvent('out', dd, e, data);
24000         if(this.overClass){
24001             this.el.removeClass(this.overClass);
24002         }
24003     },
24004
24005     /**
24006      * @hide
24007      */
24008     notifyDrop : function(dd, e, data)
24009     {
24010         this.success = false;
24011         this.fireEvent('drop', dd, e, data);
24012         return this.success;
24013     }
24014 });/*
24015  * Based on:
24016  * Ext JS Library 1.1.1
24017  * Copyright(c) 2006-2007, Ext JS, LLC.
24018  *
24019  * Originally Released Under LGPL - original licence link has changed is not relivant.
24020  *
24021  * Fork - LGPL
24022  * <script type="text/javascript">
24023  */
24024
24025
24026 /**
24027  * @class Roo.dd.DragZone
24028  * @extends Roo.dd.DragSource
24029  * This class provides a container DD instance that proxies for multiple child node sources.<br />
24030  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
24031  * @constructor
24032  * @param {String/HTMLElement/Element} el The container element
24033  * @param {Object} config
24034  */
24035 Roo.dd.DragZone = function(el, config){
24036     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
24037     if(this.containerScroll){
24038         Roo.dd.ScrollManager.register(this.el);
24039     }
24040 };
24041
24042 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
24043     /**
24044      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
24045      * for auto scrolling during drag operations.
24046      */
24047     /**
24048      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
24049      * method after a failed drop (defaults to "c3daf9" - light blue)
24050      */
24051
24052     /**
24053      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
24054      * for a valid target to drag based on the mouse down. Override this method
24055      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
24056      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
24057      * @param {EventObject} e The mouse down event
24058      * @return {Object} The dragData
24059      */
24060     getDragData : function(e){
24061         return Roo.dd.Registry.getHandleFromEvent(e);
24062     },
24063     
24064     /**
24065      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
24066      * this.dragData.ddel
24067      * @param {Number} x The x position of the click on the dragged object
24068      * @param {Number} y The y position of the click on the dragged object
24069      * @return {Boolean} true to continue the drag, false to cancel
24070      */
24071     onInitDrag : function(x, y){
24072         this.proxy.update(this.dragData.ddel.cloneNode(true));
24073         this.onStartDrag(x, y);
24074         return true;
24075     },
24076     
24077     /**
24078      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
24079      */
24080     afterRepair : function(){
24081         if(Roo.enableFx){
24082             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
24083         }
24084         this.dragging = false;
24085     },
24086
24087     /**
24088      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
24089      * the XY of this.dragData.ddel
24090      * @param {EventObject} e The mouse up event
24091      * @return {Array} The xy location (e.g. [100, 200])
24092      */
24093     getRepairXY : function(e){
24094         return Roo.Element.fly(this.dragData.ddel).getXY();  
24095     }
24096 });/*
24097  * Based on:
24098  * Ext JS Library 1.1.1
24099  * Copyright(c) 2006-2007, Ext JS, LLC.
24100  *
24101  * Originally Released Under LGPL - original licence link has changed is not relivant.
24102  *
24103  * Fork - LGPL
24104  * <script type="text/javascript">
24105  */
24106 /**
24107  * @class Roo.dd.DropZone
24108  * @extends Roo.dd.DropTarget
24109  * This class provides a container DD instance that proxies for multiple child node targets.<br />
24110  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
24111  * @constructor
24112  * @param {String/HTMLElement/Element} el The container element
24113  * @param {Object} config
24114  */
24115 Roo.dd.DropZone = function(el, config){
24116     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
24117 };
24118
24119 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
24120     /**
24121      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
24122      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
24123      * provide your own custom lookup.
24124      * @param {Event} e The event
24125      * @return {Object} data The custom data
24126      */
24127     getTargetFromEvent : function(e){
24128         return Roo.dd.Registry.getTargetFromEvent(e);
24129     },
24130
24131     /**
24132      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
24133      * that it has registered.  This method has no default implementation and should be overridden to provide
24134      * node-specific processing if necessary.
24135      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
24136      * {@link #getTargetFromEvent} for this node)
24137      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24138      * @param {Event} e The event
24139      * @param {Object} data An object containing arbitrary data supplied by the drag source
24140      */
24141     onNodeEnter : function(n, dd, e, data){
24142         
24143     },
24144
24145     /**
24146      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
24147      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
24148      * overridden to provide the proper feedback.
24149      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24150      * {@link #getTargetFromEvent} for this node)
24151      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24152      * @param {Event} e The event
24153      * @param {Object} data An object containing arbitrary data supplied by the drag source
24154      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24155      * underlying {@link Roo.dd.StatusProxy} can be updated
24156      */
24157     onNodeOver : function(n, dd, e, data){
24158         return this.dropAllowed;
24159     },
24160
24161     /**
24162      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
24163      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
24164      * node-specific processing if necessary.
24165      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24166      * {@link #getTargetFromEvent} for this node)
24167      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24168      * @param {Event} e The event
24169      * @param {Object} data An object containing arbitrary data supplied by the drag source
24170      */
24171     onNodeOut : function(n, dd, e, data){
24172         
24173     },
24174
24175     /**
24176      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
24177      * the drop node.  The default implementation returns false, so it should be overridden to provide the
24178      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
24179      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24180      * {@link #getTargetFromEvent} for this node)
24181      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24182      * @param {Event} e The event
24183      * @param {Object} data An object containing arbitrary data supplied by the drag source
24184      * @return {Boolean} True if the drop was valid, else false
24185      */
24186     onNodeDrop : function(n, dd, e, data){
24187         return false;
24188     },
24189
24190     /**
24191      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
24192      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
24193      * it should be overridden to provide the proper feedback if necessary.
24194      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24195      * @param {Event} e The event
24196      * @param {Object} data An object containing arbitrary data supplied by the drag source
24197      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24198      * underlying {@link Roo.dd.StatusProxy} can be updated
24199      */
24200     onContainerOver : function(dd, e, data){
24201         return this.dropNotAllowed;
24202     },
24203
24204     /**
24205      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
24206      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
24207      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
24208      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
24209      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24210      * @param {Event} e The event
24211      * @param {Object} data An object containing arbitrary data supplied by the drag source
24212      * @return {Boolean} True if the drop was valid, else false
24213      */
24214     onContainerDrop : function(dd, e, data){
24215         return false;
24216     },
24217
24218     /**
24219      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
24220      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
24221      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
24222      * you should override this method and provide a custom implementation.
24223      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24224      * @param {Event} e The event
24225      * @param {Object} data An object containing arbitrary data supplied by the drag source
24226      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24227      * underlying {@link Roo.dd.StatusProxy} can be updated
24228      */
24229     notifyEnter : function(dd, e, data){
24230         return this.dropNotAllowed;
24231     },
24232
24233     /**
24234      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
24235      * This method will be called on every mouse movement while the drag source is over the drop zone.
24236      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
24237      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
24238      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
24239      * registered node, it will call {@link #onContainerOver}.
24240      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24241      * @param {Event} e The event
24242      * @param {Object} data An object containing arbitrary data supplied by the drag source
24243      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24244      * underlying {@link Roo.dd.StatusProxy} can be updated
24245      */
24246     notifyOver : function(dd, e, data){
24247         var n = this.getTargetFromEvent(e);
24248         if(!n){ // not over valid drop target
24249             if(this.lastOverNode){
24250                 this.onNodeOut(this.lastOverNode, dd, e, data);
24251                 this.lastOverNode = null;
24252             }
24253             return this.onContainerOver(dd, e, data);
24254         }
24255         if(this.lastOverNode != n){
24256             if(this.lastOverNode){
24257                 this.onNodeOut(this.lastOverNode, dd, e, data);
24258             }
24259             this.onNodeEnter(n, dd, e, data);
24260             this.lastOverNode = n;
24261         }
24262         return this.onNodeOver(n, dd, e, data);
24263     },
24264
24265     /**
24266      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
24267      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
24268      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
24269      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24270      * @param {Event} e The event
24271      * @param {Object} data An object containing arbitrary data supplied by the drag zone
24272      */
24273     notifyOut : function(dd, e, data){
24274         if(this.lastOverNode){
24275             this.onNodeOut(this.lastOverNode, dd, e, data);
24276             this.lastOverNode = null;
24277         }
24278     },
24279
24280     /**
24281      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
24282      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
24283      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
24284      * otherwise it will call {@link #onContainerDrop}.
24285      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24286      * @param {Event} e The event
24287      * @param {Object} data An object containing arbitrary data supplied by the drag source
24288      * @return {Boolean} True if the drop was valid, else false
24289      */
24290     notifyDrop : function(dd, e, data){
24291         if(this.lastOverNode){
24292             this.onNodeOut(this.lastOverNode, dd, e, data);
24293             this.lastOverNode = null;
24294         }
24295         var n = this.getTargetFromEvent(e);
24296         return n ?
24297             this.onNodeDrop(n, dd, e, data) :
24298             this.onContainerDrop(dd, e, data);
24299     },
24300
24301     // private
24302     triggerCacheRefresh : function(){
24303         Roo.dd.DDM.refreshCache(this.groups);
24304     }  
24305 });/*
24306  * Based on:
24307  * Ext JS Library 1.1.1
24308  * Copyright(c) 2006-2007, Ext JS, LLC.
24309  *
24310  * Originally Released Under LGPL - original licence link has changed is not relivant.
24311  *
24312  * Fork - LGPL
24313  * <script type="text/javascript">
24314  */
24315
24316
24317 /**
24318  * @class Roo.data.SortTypes
24319  * @static
24320  * Defines the default sorting (casting?) comparison functions used when sorting data.
24321  */
24322 Roo.data.SortTypes = {
24323     /**
24324      * Default sort that does nothing
24325      * @param {Mixed} s The value being converted
24326      * @return {Mixed} The comparison value
24327      */
24328     none : function(s){
24329         return s;
24330     },
24331     
24332     /**
24333      * The regular expression used to strip tags
24334      * @type {RegExp}
24335      * @property
24336      */
24337     stripTagsRE : /<\/?[^>]+>/gi,
24338     
24339     /**
24340      * Strips all HTML tags to sort on text only
24341      * @param {Mixed} s The value being converted
24342      * @return {String} The comparison value
24343      */
24344     asText : function(s){
24345         return String(s).replace(this.stripTagsRE, "");
24346     },
24347     
24348     /**
24349      * Strips all HTML tags to sort on text only - Case insensitive
24350      * @param {Mixed} s The value being converted
24351      * @return {String} The comparison value
24352      */
24353     asUCText : function(s){
24354         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24355     },
24356     
24357     /**
24358      * Case insensitive string
24359      * @param {Mixed} s The value being converted
24360      * @return {String} The comparison value
24361      */
24362     asUCString : function(s) {
24363         return String(s).toUpperCase();
24364     },
24365     
24366     /**
24367      * Date sorting
24368      * @param {Mixed} s The value being converted
24369      * @return {Number} The comparison value
24370      */
24371     asDate : function(s) {
24372         if(!s){
24373             return 0;
24374         }
24375         if(s instanceof Date){
24376             return s.getTime();
24377         }
24378         return Date.parse(String(s));
24379     },
24380     
24381     /**
24382      * Float sorting
24383      * @param {Mixed} s The value being converted
24384      * @return {Float} The comparison value
24385      */
24386     asFloat : function(s) {
24387         var val = parseFloat(String(s).replace(/,/g, ""));
24388         if(isNaN(val)) {
24389             val = 0;
24390         }
24391         return val;
24392     },
24393     
24394     /**
24395      * Integer sorting
24396      * @param {Mixed} s The value being converted
24397      * @return {Number} The comparison value
24398      */
24399     asInt : function(s) {
24400         var val = parseInt(String(s).replace(/,/g, ""));
24401         if(isNaN(val)) {
24402             val = 0;
24403         }
24404         return val;
24405     }
24406 };/*
24407  * Based on:
24408  * Ext JS Library 1.1.1
24409  * Copyright(c) 2006-2007, Ext JS, LLC.
24410  *
24411  * Originally Released Under LGPL - original licence link has changed is not relivant.
24412  *
24413  * Fork - LGPL
24414  * <script type="text/javascript">
24415  */
24416
24417 /**
24418 * @class Roo.data.Record
24419  * Instances of this class encapsulate both record <em>definition</em> information, and record
24420  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24421  * to access Records cached in an {@link Roo.data.Store} object.<br>
24422  * <p>
24423  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24424  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24425  * objects.<br>
24426  * <p>
24427  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24428  * @constructor
24429  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24430  * {@link #create}. The parameters are the same.
24431  * @param {Array} data An associative Array of data values keyed by the field name.
24432  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24433  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24434  * not specified an integer id is generated.
24435  */
24436 Roo.data.Record = function(data, id){
24437     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24438     this.data = data;
24439 };
24440
24441 /**
24442  * Generate a constructor for a specific record layout.
24443  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24444  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24445  * Each field definition object may contain the following properties: <ul>
24446  * <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,
24447  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24448  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24449  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24450  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24451  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24452  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24453  * this may be omitted.</p></li>
24454  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24455  * <ul><li>auto (Default, implies no conversion)</li>
24456  * <li>string</li>
24457  * <li>int</li>
24458  * <li>float</li>
24459  * <li>boolean</li>
24460  * <li>date</li></ul></p></li>
24461  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24462  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24463  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24464  * by the Reader into an object that will be stored in the Record. It is passed the
24465  * following parameters:<ul>
24466  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24467  * </ul></p></li>
24468  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24469  * </ul>
24470  * <br>usage:<br><pre><code>
24471 var TopicRecord = Roo.data.Record.create(
24472     {name: 'title', mapping: 'topic_title'},
24473     {name: 'author', mapping: 'username'},
24474     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24475     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24476     {name: 'lastPoster', mapping: 'user2'},
24477     {name: 'excerpt', mapping: 'post_text'}
24478 );
24479
24480 var myNewRecord = new TopicRecord({
24481     title: 'Do my job please',
24482     author: 'noobie',
24483     totalPosts: 1,
24484     lastPost: new Date(),
24485     lastPoster: 'Animal',
24486     excerpt: 'No way dude!'
24487 });
24488 myStore.add(myNewRecord);
24489 </code></pre>
24490  * @method create
24491  * @static
24492  */
24493 Roo.data.Record.create = function(o){
24494     var f = function(){
24495         f.superclass.constructor.apply(this, arguments);
24496     };
24497     Roo.extend(f, Roo.data.Record);
24498     var p = f.prototype;
24499     p.fields = new Roo.util.MixedCollection(false, function(field){
24500         return field.name;
24501     });
24502     for(var i = 0, len = o.length; i < len; i++){
24503         p.fields.add(new Roo.data.Field(o[i]));
24504     }
24505     f.getField = function(name){
24506         return p.fields.get(name);  
24507     };
24508     return f;
24509 };
24510
24511 Roo.data.Record.AUTO_ID = 1000;
24512 Roo.data.Record.EDIT = 'edit';
24513 Roo.data.Record.REJECT = 'reject';
24514 Roo.data.Record.COMMIT = 'commit';
24515
24516 Roo.data.Record.prototype = {
24517     /**
24518      * Readonly flag - true if this record has been modified.
24519      * @type Boolean
24520      */
24521     dirty : false,
24522     editing : false,
24523     error: null,
24524     modified: null,
24525
24526     // private
24527     join : function(store){
24528         this.store = store;
24529     },
24530
24531     /**
24532      * Set the named field to the specified value.
24533      * @param {String} name The name of the field to set.
24534      * @param {Object} value The value to set the field to.
24535      */
24536     set : function(name, value){
24537         if(this.data[name] == value){
24538             return;
24539         }
24540         this.dirty = true;
24541         if(!this.modified){
24542             this.modified = {};
24543         }
24544         if(typeof this.modified[name] == 'undefined'){
24545             this.modified[name] = this.data[name];
24546         }
24547         this.data[name] = value;
24548         if(!this.editing && this.store){
24549             this.store.afterEdit(this);
24550         }       
24551     },
24552
24553     /**
24554      * Get the value of the named field.
24555      * @param {String} name The name of the field to get the value of.
24556      * @return {Object} The value of the field.
24557      */
24558     get : function(name){
24559         return this.data[name]; 
24560     },
24561
24562     // private
24563     beginEdit : function(){
24564         this.editing = true;
24565         this.modified = {}; 
24566     },
24567
24568     // private
24569     cancelEdit : function(){
24570         this.editing = false;
24571         delete this.modified;
24572     },
24573
24574     // private
24575     endEdit : function(){
24576         this.editing = false;
24577         if(this.dirty && this.store){
24578             this.store.afterEdit(this);
24579         }
24580     },
24581
24582     /**
24583      * Usually called by the {@link Roo.data.Store} which owns the Record.
24584      * Rejects all changes made to the Record since either creation, or the last commit operation.
24585      * Modified fields are reverted to their original values.
24586      * <p>
24587      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24588      * of reject operations.
24589      */
24590     reject : function(){
24591         var m = this.modified;
24592         for(var n in m){
24593             if(typeof m[n] != "function"){
24594                 this.data[n] = m[n];
24595             }
24596         }
24597         this.dirty = false;
24598         delete this.modified;
24599         this.editing = false;
24600         if(this.store){
24601             this.store.afterReject(this);
24602         }
24603     },
24604
24605     /**
24606      * Usually called by the {@link Roo.data.Store} which owns the Record.
24607      * Commits all changes made to the Record since either creation, or the last commit operation.
24608      * <p>
24609      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24610      * of commit operations.
24611      */
24612     commit : function(){
24613         this.dirty = false;
24614         delete this.modified;
24615         this.editing = false;
24616         if(this.store){
24617             this.store.afterCommit(this);
24618         }
24619     },
24620
24621     // private
24622     hasError : function(){
24623         return this.error != null;
24624     },
24625
24626     // private
24627     clearError : function(){
24628         this.error = null;
24629     },
24630
24631     /**
24632      * Creates a copy of this record.
24633      * @param {String} id (optional) A new record id if you don't want to use this record's id
24634      * @return {Record}
24635      */
24636     copy : function(newId) {
24637         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24638     }
24639 };/*
24640  * Based on:
24641  * Ext JS Library 1.1.1
24642  * Copyright(c) 2006-2007, Ext JS, LLC.
24643  *
24644  * Originally Released Under LGPL - original licence link has changed is not relivant.
24645  *
24646  * Fork - LGPL
24647  * <script type="text/javascript">
24648  */
24649
24650
24651
24652 /**
24653  * @class Roo.data.Store
24654  * @extends Roo.util.Observable
24655  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24656  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24657  * <p>
24658  * 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
24659  * has no knowledge of the format of the data returned by the Proxy.<br>
24660  * <p>
24661  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24662  * instances from the data object. These records are cached and made available through accessor functions.
24663  * @constructor
24664  * Creates a new Store.
24665  * @param {Object} config A config object containing the objects needed for the Store to access data,
24666  * and read the data into Records.
24667  */
24668 Roo.data.Store = function(config){
24669     this.data = new Roo.util.MixedCollection(false);
24670     this.data.getKey = function(o){
24671         return o.id;
24672     };
24673     this.baseParams = {};
24674     // private
24675     this.paramNames = {
24676         "start" : "start",
24677         "limit" : "limit",
24678         "sort" : "sort",
24679         "dir" : "dir",
24680         "multisort" : "_multisort"
24681     };
24682
24683     if(config && config.data){
24684         this.inlineData = config.data;
24685         delete config.data;
24686     }
24687
24688     Roo.apply(this, config);
24689     
24690     if(this.reader){ // reader passed
24691         this.reader = Roo.factory(this.reader, Roo.data);
24692         this.reader.xmodule = this.xmodule || false;
24693         if(!this.recordType){
24694             this.recordType = this.reader.recordType;
24695         }
24696         if(this.reader.onMetaChange){
24697             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24698         }
24699     }
24700
24701     if(this.recordType){
24702         this.fields = this.recordType.prototype.fields;
24703     }
24704     this.modified = [];
24705
24706     this.addEvents({
24707         /**
24708          * @event datachanged
24709          * Fires when the data cache has changed, and a widget which is using this Store
24710          * as a Record cache should refresh its view.
24711          * @param {Store} this
24712          */
24713         datachanged : true,
24714         /**
24715          * @event metachange
24716          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24717          * @param {Store} this
24718          * @param {Object} meta The JSON metadata
24719          */
24720         metachange : true,
24721         /**
24722          * @event add
24723          * Fires when Records have been added to the Store
24724          * @param {Store} this
24725          * @param {Roo.data.Record[]} records The array of Records added
24726          * @param {Number} index The index at which the record(s) were added
24727          */
24728         add : true,
24729         /**
24730          * @event remove
24731          * Fires when a Record has been removed from the Store
24732          * @param {Store} this
24733          * @param {Roo.data.Record} record The Record that was removed
24734          * @param {Number} index The index at which the record was removed
24735          */
24736         remove : true,
24737         /**
24738          * @event update
24739          * Fires when a Record has been updated
24740          * @param {Store} this
24741          * @param {Roo.data.Record} record The Record that was updated
24742          * @param {String} operation The update operation being performed.  Value may be one of:
24743          * <pre><code>
24744  Roo.data.Record.EDIT
24745  Roo.data.Record.REJECT
24746  Roo.data.Record.COMMIT
24747          * </code></pre>
24748          */
24749         update : true,
24750         /**
24751          * @event clear
24752          * Fires when the data cache has been cleared.
24753          * @param {Store} this
24754          */
24755         clear : true,
24756         /**
24757          * @event beforeload
24758          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24759          * the load action will be canceled.
24760          * @param {Store} this
24761          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24762          */
24763         beforeload : true,
24764         /**
24765          * @event beforeloadadd
24766          * Fires after a new set of Records has been loaded.
24767          * @param {Store} this
24768          * @param {Roo.data.Record[]} records The Records that were loaded
24769          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24770          */
24771         beforeloadadd : true,
24772         /**
24773          * @event load
24774          * Fires after a new set of Records has been loaded, before they are added to the store.
24775          * @param {Store} this
24776          * @param {Roo.data.Record[]} records The Records that were loaded
24777          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24778          * @params {Object} return from reader
24779          */
24780         load : true,
24781         /**
24782          * @event loadexception
24783          * Fires if an exception occurs in the Proxy during loading.
24784          * Called with the signature of the Proxy's "loadexception" event.
24785          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24786          * 
24787          * @param {Proxy} 
24788          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24789          * @param {Object} load options 
24790          * @param {Object} jsonData from your request (normally this contains the Exception)
24791          */
24792         loadexception : true
24793     });
24794     
24795     if(this.proxy){
24796         this.proxy = Roo.factory(this.proxy, Roo.data);
24797         this.proxy.xmodule = this.xmodule || false;
24798         this.relayEvents(this.proxy,  ["loadexception"]);
24799     }
24800     this.sortToggle = {};
24801     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24802
24803     Roo.data.Store.superclass.constructor.call(this);
24804
24805     if(this.inlineData){
24806         this.loadData(this.inlineData);
24807         delete this.inlineData;
24808     }
24809 };
24810
24811 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24812      /**
24813     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24814     * without a remote query - used by combo/forms at present.
24815     */
24816     
24817     /**
24818     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24819     */
24820     /**
24821     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24822     */
24823     /**
24824     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24825     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24826     */
24827     /**
24828     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24829     * on any HTTP request
24830     */
24831     /**
24832     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24833     */
24834     /**
24835     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24836     */
24837     multiSort: false,
24838     /**
24839     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24840     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24841     */
24842     remoteSort : false,
24843
24844     /**
24845     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24846      * loaded or when a record is removed. (defaults to false).
24847     */
24848     pruneModifiedRecords : false,
24849
24850     // private
24851     lastOptions : null,
24852
24853     /**
24854      * Add Records to the Store and fires the add event.
24855      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24856      */
24857     add : function(records){
24858         records = [].concat(records);
24859         for(var i = 0, len = records.length; i < len; i++){
24860             records[i].join(this);
24861         }
24862         var index = this.data.length;
24863         this.data.addAll(records);
24864         this.fireEvent("add", this, records, index);
24865     },
24866
24867     /**
24868      * Remove a Record from the Store and fires the remove event.
24869      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24870      */
24871     remove : function(record){
24872         var index = this.data.indexOf(record);
24873         this.data.removeAt(index);
24874  
24875         if(this.pruneModifiedRecords){
24876             this.modified.remove(record);
24877         }
24878         this.fireEvent("remove", this, record, index);
24879     },
24880
24881     /**
24882      * Remove all Records from the Store and fires the clear event.
24883      */
24884     removeAll : function(){
24885         this.data.clear();
24886         if(this.pruneModifiedRecords){
24887             this.modified = [];
24888         }
24889         this.fireEvent("clear", this);
24890     },
24891
24892     /**
24893      * Inserts Records to the Store at the given index and fires the add event.
24894      * @param {Number} index The start index at which to insert the passed Records.
24895      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24896      */
24897     insert : function(index, records){
24898         records = [].concat(records);
24899         for(var i = 0, len = records.length; i < len; i++){
24900             this.data.insert(index, records[i]);
24901             records[i].join(this);
24902         }
24903         this.fireEvent("add", this, records, index);
24904     },
24905
24906     /**
24907      * Get the index within the cache of the passed Record.
24908      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24909      * @return {Number} The index of the passed Record. Returns -1 if not found.
24910      */
24911     indexOf : function(record){
24912         return this.data.indexOf(record);
24913     },
24914
24915     /**
24916      * Get the index within the cache of the Record with the passed id.
24917      * @param {String} id The id of the Record to find.
24918      * @return {Number} The index of the Record. Returns -1 if not found.
24919      */
24920     indexOfId : function(id){
24921         return this.data.indexOfKey(id);
24922     },
24923
24924     /**
24925      * Get the Record with the specified id.
24926      * @param {String} id The id of the Record to find.
24927      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24928      */
24929     getById : function(id){
24930         return this.data.key(id);
24931     },
24932
24933     /**
24934      * Get the Record at the specified index.
24935      * @param {Number} index The index of the Record to find.
24936      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24937      */
24938     getAt : function(index){
24939         return this.data.itemAt(index);
24940     },
24941
24942     /**
24943      * Returns a range of Records between specified indices.
24944      * @param {Number} startIndex (optional) The starting index (defaults to 0)
24945      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24946      * @return {Roo.data.Record[]} An array of Records
24947      */
24948     getRange : function(start, end){
24949         return this.data.getRange(start, end);
24950     },
24951
24952     // private
24953     storeOptions : function(o){
24954         o = Roo.apply({}, o);
24955         delete o.callback;
24956         delete o.scope;
24957         this.lastOptions = o;
24958     },
24959
24960     /**
24961      * Loads the Record cache from the configured Proxy using the configured Reader.
24962      * <p>
24963      * If using remote paging, then the first load call must specify the <em>start</em>
24964      * and <em>limit</em> properties in the options.params property to establish the initial
24965      * position within the dataset, and the number of Records to cache on each read from the Proxy.
24966      * <p>
24967      * <strong>It is important to note that for remote data sources, loading is asynchronous,
24968      * and this call will return before the new data has been loaded. Perform any post-processing
24969      * in a callback function, or in a "load" event handler.</strong>
24970      * <p>
24971      * @param {Object} options An object containing properties which control loading options:<ul>
24972      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24973      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
24974      * <pre>
24975                 {
24976                     data : data,  // array of key=>value data like JsonReader
24977                     total : data.length,
24978                     success : true
24979                     
24980                 }
24981         </pre>
24982             }.</li>
24983      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24984      * passed the following arguments:<ul>
24985      * <li>r : Roo.data.Record[]</li>
24986      * <li>options: Options object from the load call</li>
24987      * <li>success: Boolean success indicator</li></ul></li>
24988      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24989      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24990      * </ul>
24991      */
24992     load : function(options){
24993         options = options || {};
24994         if(this.fireEvent("beforeload", this, options) !== false){
24995             this.storeOptions(options);
24996             var p = Roo.apply(options.params || {}, this.baseParams);
24997             // if meta was not loaded from remote source.. try requesting it.
24998             if (!this.reader.metaFromRemote) {
24999                 p._requestMeta = 1;
25000             }
25001             if(this.sortInfo && this.remoteSort){
25002                 var pn = this.paramNames;
25003                 p[pn["sort"]] = this.sortInfo.field;
25004                 p[pn["dir"]] = this.sortInfo.direction;
25005             }
25006             if (this.multiSort) {
25007                 var pn = this.paramNames;
25008                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
25009             }
25010             
25011             this.proxy.load(p, this.reader, this.loadRecords, this, options);
25012         }
25013     },
25014
25015     /**
25016      * Reloads the Record cache from the configured Proxy using the configured Reader and
25017      * the options from the last load operation performed.
25018      * @param {Object} options (optional) An object containing properties which may override the options
25019      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
25020      * the most recently used options are reused).
25021      */
25022     reload : function(options){
25023         this.load(Roo.applyIf(options||{}, this.lastOptions));
25024     },
25025
25026     // private
25027     // Called as a callback by the Reader during a load operation.
25028     loadRecords : function(o, options, success){
25029          
25030         if(!o){
25031             if(success !== false){
25032                 this.fireEvent("load", this, [], options, o);
25033             }
25034             if(options.callback){
25035                 options.callback.call(options.scope || this, [], options, false);
25036             }
25037             return;
25038         }
25039         // if data returned failure - throw an exception.
25040         if (o.success === false) {
25041             // show a message if no listener is registered.
25042             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
25043                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
25044             }
25045             // loadmask wil be hooked into this..
25046             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
25047             return;
25048         }
25049         var r = o.records, t = o.totalRecords || r.length;
25050         
25051         this.fireEvent("beforeloadadd", this, r, options, o);
25052         
25053         if(!options || options.add !== true){
25054             if(this.pruneModifiedRecords){
25055                 this.modified = [];
25056             }
25057             for(var i = 0, len = r.length; i < len; i++){
25058                 r[i].join(this);
25059             }
25060             if(this.snapshot){
25061                 this.data = this.snapshot;
25062                 delete this.snapshot;
25063             }
25064             this.data.clear();
25065             this.data.addAll(r);
25066             this.totalLength = t;
25067             this.applySort();
25068             this.fireEvent("datachanged", this);
25069         }else{
25070             this.totalLength = Math.max(t, this.data.length+r.length);
25071             this.add(r);
25072         }
25073         
25074         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
25075                 
25076             var e = new Roo.data.Record({});
25077
25078             e.set(this.parent.displayField, this.parent.emptyTitle);
25079             e.set(this.parent.valueField, '');
25080
25081             this.insert(0, e);
25082         }
25083             
25084         this.fireEvent("load", this, r, options, o);
25085         if(options.callback){
25086             options.callback.call(options.scope || this, r, options, true);
25087         }
25088     },
25089
25090
25091     /**
25092      * Loads data from a passed data block. A Reader which understands the format of the data
25093      * must have been configured in the constructor.
25094      * @param {Object} data The data block from which to read the Records.  The format of the data expected
25095      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
25096      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
25097      */
25098     loadData : function(o, append){
25099         var r = this.reader.readRecords(o);
25100         this.loadRecords(r, {add: append}, true);
25101     },
25102     
25103      /**
25104      * using 'cn' the nested child reader read the child array into it's child stores.
25105      * @param {Object} rec The record with a 'children array
25106      */
25107     loadDataFromChildren : function(rec)
25108     {
25109         this.loadData(this.reader.toLoadData(rec));
25110     },
25111     
25112
25113     /**
25114      * Gets the number of cached records.
25115      * <p>
25116      * <em>If using paging, this may not be the total size of the dataset. If the data object
25117      * used by the Reader contains the dataset size, then the getTotalCount() function returns
25118      * the data set size</em>
25119      */
25120     getCount : function(){
25121         return this.data.length || 0;
25122     },
25123
25124     /**
25125      * Gets the total number of records in the dataset as returned by the server.
25126      * <p>
25127      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
25128      * the dataset size</em>
25129      */
25130     getTotalCount : function(){
25131         return this.totalLength || 0;
25132     },
25133
25134     /**
25135      * Returns the sort state of the Store as an object with two properties:
25136      * <pre><code>
25137  field {String} The name of the field by which the Records are sorted
25138  direction {String} The sort order, "ASC" or "DESC"
25139      * </code></pre>
25140      */
25141     getSortState : function(){
25142         return this.sortInfo;
25143     },
25144
25145     // private
25146     applySort : function(){
25147         if(this.sortInfo && !this.remoteSort){
25148             var s = this.sortInfo, f = s.field;
25149             var st = this.fields.get(f).sortType;
25150             var fn = function(r1, r2){
25151                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
25152                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
25153             };
25154             this.data.sort(s.direction, fn);
25155             if(this.snapshot && this.snapshot != this.data){
25156                 this.snapshot.sort(s.direction, fn);
25157             }
25158         }
25159     },
25160
25161     /**
25162      * Sets the default sort column and order to be used by the next load operation.
25163      * @param {String} fieldName The name of the field to sort by.
25164      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25165      */
25166     setDefaultSort : function(field, dir){
25167         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
25168     },
25169
25170     /**
25171      * Sort the Records.
25172      * If remote sorting is used, the sort is performed on the server, and the cache is
25173      * reloaded. If local sorting is used, the cache is sorted internally.
25174      * @param {String} fieldName The name of the field to sort by.
25175      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25176      */
25177     sort : function(fieldName, dir){
25178         var f = this.fields.get(fieldName);
25179         if(!dir){
25180             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
25181             
25182             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
25183                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
25184             }else{
25185                 dir = f.sortDir;
25186             }
25187         }
25188         this.sortToggle[f.name] = dir;
25189         this.sortInfo = {field: f.name, direction: dir};
25190         if(!this.remoteSort){
25191             this.applySort();
25192             this.fireEvent("datachanged", this);
25193         }else{
25194             this.load(this.lastOptions);
25195         }
25196     },
25197
25198     /**
25199      * Calls the specified function for each of the Records in the cache.
25200      * @param {Function} fn The function to call. The Record is passed as the first parameter.
25201      * Returning <em>false</em> aborts and exits the iteration.
25202      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
25203      */
25204     each : function(fn, scope){
25205         this.data.each(fn, scope);
25206     },
25207
25208     /**
25209      * Gets all records modified since the last commit.  Modified records are persisted across load operations
25210      * (e.g., during paging).
25211      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
25212      */
25213     getModifiedRecords : function(){
25214         return this.modified;
25215     },
25216
25217     // private
25218     createFilterFn : function(property, value, anyMatch){
25219         if(!value.exec){ // not a regex
25220             value = String(value);
25221             if(value.length == 0){
25222                 return false;
25223             }
25224             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
25225         }
25226         return function(r){
25227             return value.test(r.data[property]);
25228         };
25229     },
25230
25231     /**
25232      * Sums the value of <i>property</i> for each record between start and end and returns the result.
25233      * @param {String} property A field on your records
25234      * @param {Number} start The record index to start at (defaults to 0)
25235      * @param {Number} end The last record index to include (defaults to length - 1)
25236      * @return {Number} The sum
25237      */
25238     sum : function(property, start, end){
25239         var rs = this.data.items, v = 0;
25240         start = start || 0;
25241         end = (end || end === 0) ? end : rs.length-1;
25242
25243         for(var i = start; i <= end; i++){
25244             v += (rs[i].data[property] || 0);
25245         }
25246         return v;
25247     },
25248
25249     /**
25250      * Filter the records by a specified property.
25251      * @param {String} field A field on your records
25252      * @param {String/RegExp} value Either a string that the field
25253      * should start with or a RegExp to test against the field
25254      * @param {Boolean} anyMatch True to match any part not just the beginning
25255      */
25256     filter : function(property, value, anyMatch){
25257         var fn = this.createFilterFn(property, value, anyMatch);
25258         return fn ? this.filterBy(fn) : this.clearFilter();
25259     },
25260
25261     /**
25262      * Filter by a function. The specified function will be called with each
25263      * record in this data source. If the function returns true the record is included,
25264      * otherwise it is filtered.
25265      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25266      * @param {Object} scope (optional) The scope of the function (defaults to this)
25267      */
25268     filterBy : function(fn, scope){
25269         this.snapshot = this.snapshot || this.data;
25270         this.data = this.queryBy(fn, scope||this);
25271         this.fireEvent("datachanged", this);
25272     },
25273
25274     /**
25275      * Query the records by a specified property.
25276      * @param {String} field A field on your records
25277      * @param {String/RegExp} value Either a string that the field
25278      * should start with or a RegExp to test against the field
25279      * @param {Boolean} anyMatch True to match any part not just the beginning
25280      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25281      */
25282     query : function(property, value, anyMatch){
25283         var fn = this.createFilterFn(property, value, anyMatch);
25284         return fn ? this.queryBy(fn) : this.data.clone();
25285     },
25286
25287     /**
25288      * Query by a function. The specified function will be called with each
25289      * record in this data source. If the function returns true the record is included
25290      * in the results.
25291      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25292      * @param {Object} scope (optional) The scope of the function (defaults to this)
25293       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25294      **/
25295     queryBy : function(fn, scope){
25296         var data = this.snapshot || this.data;
25297         return data.filterBy(fn, scope||this);
25298     },
25299
25300     /**
25301      * Collects unique values for a particular dataIndex from this store.
25302      * @param {String} dataIndex The property to collect
25303      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25304      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25305      * @return {Array} An array of the unique values
25306      **/
25307     collect : function(dataIndex, allowNull, bypassFilter){
25308         var d = (bypassFilter === true && this.snapshot) ?
25309                 this.snapshot.items : this.data.items;
25310         var v, sv, r = [], l = {};
25311         for(var i = 0, len = d.length; i < len; i++){
25312             v = d[i].data[dataIndex];
25313             sv = String(v);
25314             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25315                 l[sv] = true;
25316                 r[r.length] = v;
25317             }
25318         }
25319         return r;
25320     },
25321
25322     /**
25323      * Revert to a view of the Record cache with no filtering applied.
25324      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25325      */
25326     clearFilter : function(suppressEvent){
25327         if(this.snapshot && this.snapshot != this.data){
25328             this.data = this.snapshot;
25329             delete this.snapshot;
25330             if(suppressEvent !== true){
25331                 this.fireEvent("datachanged", this);
25332             }
25333         }
25334     },
25335
25336     // private
25337     afterEdit : function(record){
25338         if(this.modified.indexOf(record) == -1){
25339             this.modified.push(record);
25340         }
25341         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25342     },
25343     
25344     // private
25345     afterReject : function(record){
25346         this.modified.remove(record);
25347         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25348     },
25349
25350     // private
25351     afterCommit : function(record){
25352         this.modified.remove(record);
25353         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25354     },
25355
25356     /**
25357      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25358      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25359      */
25360     commitChanges : function(){
25361         var m = this.modified.slice(0);
25362         this.modified = [];
25363         for(var i = 0, len = m.length; i < len; i++){
25364             m[i].commit();
25365         }
25366     },
25367
25368     /**
25369      * Cancel outstanding changes on all changed records.
25370      */
25371     rejectChanges : function(){
25372         var m = this.modified.slice(0);
25373         this.modified = [];
25374         for(var i = 0, len = m.length; i < len; i++){
25375             m[i].reject();
25376         }
25377     },
25378
25379     onMetaChange : function(meta, rtype, o){
25380         this.recordType = rtype;
25381         this.fields = rtype.prototype.fields;
25382         delete this.snapshot;
25383         this.sortInfo = meta.sortInfo || this.sortInfo;
25384         this.modified = [];
25385         this.fireEvent('metachange', this, this.reader.meta);
25386     },
25387     
25388     moveIndex : function(data, type)
25389     {
25390         var index = this.indexOf(data);
25391         
25392         var newIndex = index + type;
25393         
25394         this.remove(data);
25395         
25396         this.insert(newIndex, data);
25397         
25398     }
25399 });/*
25400  * Based on:
25401  * Ext JS Library 1.1.1
25402  * Copyright(c) 2006-2007, Ext JS, LLC.
25403  *
25404  * Originally Released Under LGPL - original licence link has changed is not relivant.
25405  *
25406  * Fork - LGPL
25407  * <script type="text/javascript">
25408  */
25409
25410 /**
25411  * @class Roo.data.SimpleStore
25412  * @extends Roo.data.Store
25413  * Small helper class to make creating Stores from Array data easier.
25414  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25415  * @cfg {Array} fields An array of field definition objects, or field name strings.
25416  * @cfg {Object} an existing reader (eg. copied from another store)
25417  * @cfg {Array} data The multi-dimensional array of data
25418  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25419  * @cfg {Roo.data.Reader} reader  [not-required] 
25420  * @constructor
25421  * @param {Object} config
25422  */
25423 Roo.data.SimpleStore = function(config)
25424 {
25425     Roo.data.SimpleStore.superclass.constructor.call(this, {
25426         isLocal : true,
25427         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25428                 id: config.id
25429             },
25430             Roo.data.Record.create(config.fields)
25431         ),
25432         proxy : new Roo.data.MemoryProxy(config.data)
25433     });
25434     this.load();
25435 };
25436 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25437  * Based on:
25438  * Ext JS Library 1.1.1
25439  * Copyright(c) 2006-2007, Ext JS, LLC.
25440  *
25441  * Originally Released Under LGPL - original licence link has changed is not relivant.
25442  *
25443  * Fork - LGPL
25444  * <script type="text/javascript">
25445  */
25446
25447 /**
25448 /**
25449  * @extends Roo.data.Store
25450  * @class Roo.data.JsonStore
25451  * Small helper class to make creating Stores for JSON data easier. <br/>
25452 <pre><code>
25453 var store = new Roo.data.JsonStore({
25454     url: 'get-images.php',
25455     root: 'images',
25456     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25457 });
25458 </code></pre>
25459  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25460  * JsonReader and HttpProxy (unless inline data is provided).</b>
25461  * @cfg {Array} fields An array of field definition objects, or field name strings.
25462  * @constructor
25463  * @param {Object} config
25464  */
25465 Roo.data.JsonStore = function(c){
25466     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25467         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25468         reader: new Roo.data.JsonReader(c, c.fields)
25469     }));
25470 };
25471 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25472  * Based on:
25473  * Ext JS Library 1.1.1
25474  * Copyright(c) 2006-2007, Ext JS, LLC.
25475  *
25476  * Originally Released Under LGPL - original licence link has changed is not relivant.
25477  *
25478  * Fork - LGPL
25479  * <script type="text/javascript">
25480  */
25481
25482  
25483 Roo.data.Field = function(config){
25484     if(typeof config == "string"){
25485         config = {name: config};
25486     }
25487     Roo.apply(this, config);
25488     
25489     if(!this.type){
25490         this.type = "auto";
25491     }
25492     
25493     var st = Roo.data.SortTypes;
25494     // named sortTypes are supported, here we look them up
25495     if(typeof this.sortType == "string"){
25496         this.sortType = st[this.sortType];
25497     }
25498     
25499     // set default sortType for strings and dates
25500     if(!this.sortType){
25501         switch(this.type){
25502             case "string":
25503                 this.sortType = st.asUCString;
25504                 break;
25505             case "date":
25506                 this.sortType = st.asDate;
25507                 break;
25508             default:
25509                 this.sortType = st.none;
25510         }
25511     }
25512
25513     // define once
25514     var stripRe = /[\$,%]/g;
25515
25516     // prebuilt conversion function for this field, instead of
25517     // switching every time we're reading a value
25518     if(!this.convert){
25519         var cv, dateFormat = this.dateFormat;
25520         switch(this.type){
25521             case "":
25522             case "auto":
25523             case undefined:
25524                 cv = function(v){ return v; };
25525                 break;
25526             case "string":
25527                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25528                 break;
25529             case "int":
25530                 cv = function(v){
25531                     return v !== undefined && v !== null && v !== '' ?
25532                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25533                     };
25534                 break;
25535             case "float":
25536                 cv = function(v){
25537                     return v !== undefined && v !== null && v !== '' ?
25538                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25539                     };
25540                 break;
25541             case "bool":
25542             case "boolean":
25543                 cv = function(v){ return v === true || v === "true" || v == 1; };
25544                 break;
25545             case "date":
25546                 cv = function(v){
25547                     if(!v){
25548                         return '';
25549                     }
25550                     if(v instanceof Date){
25551                         return v;
25552                     }
25553                     if(dateFormat){
25554                         if(dateFormat == "timestamp"){
25555                             return new Date(v*1000);
25556                         }
25557                         return Date.parseDate(v, dateFormat);
25558                     }
25559                     var parsed = Date.parse(v);
25560                     return parsed ? new Date(parsed) : null;
25561                 };
25562              break;
25563             
25564         }
25565         this.convert = cv;
25566     }
25567 };
25568
25569 Roo.data.Field.prototype = {
25570     dateFormat: null,
25571     defaultValue: "",
25572     mapping: null,
25573     sortType : null,
25574     sortDir : "ASC"
25575 };/*
25576  * Based on:
25577  * Ext JS Library 1.1.1
25578  * Copyright(c) 2006-2007, Ext JS, LLC.
25579  *
25580  * Originally Released Under LGPL - original licence link has changed is not relivant.
25581  *
25582  * Fork - LGPL
25583  * <script type="text/javascript">
25584  */
25585  
25586 // Base class for reading structured data from a data source.  This class is intended to be
25587 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25588
25589 /**
25590  * @class Roo.data.DataReader
25591  * @abstract
25592  * Base class for reading structured data from a data source.  This class is intended to be
25593  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25594  */
25595
25596 Roo.data.DataReader = function(meta, recordType){
25597     
25598     this.meta = meta;
25599     
25600     this.recordType = recordType instanceof Array ? 
25601         Roo.data.Record.create(recordType) : recordType;
25602 };
25603
25604 Roo.data.DataReader.prototype = {
25605     
25606     
25607     readerType : 'Data',
25608      /**
25609      * Create an empty record
25610      * @param {Object} data (optional) - overlay some values
25611      * @return {Roo.data.Record} record created.
25612      */
25613     newRow :  function(d) {
25614         var da =  {};
25615         this.recordType.prototype.fields.each(function(c) {
25616             switch( c.type) {
25617                 case 'int' : da[c.name] = 0; break;
25618                 case 'date' : da[c.name] = new Date(); break;
25619                 case 'float' : da[c.name] = 0.0; break;
25620                 case 'boolean' : da[c.name] = false; break;
25621                 default : da[c.name] = ""; break;
25622             }
25623             
25624         });
25625         return new this.recordType(Roo.apply(da, d));
25626     }
25627     
25628     
25629 };/*
25630  * Based on:
25631  * Ext JS Library 1.1.1
25632  * Copyright(c) 2006-2007, Ext JS, LLC.
25633  *
25634  * Originally Released Under LGPL - original licence link has changed is not relivant.
25635  *
25636  * Fork - LGPL
25637  * <script type="text/javascript">
25638  */
25639
25640 /**
25641  * @class Roo.data.DataProxy
25642  * @extends Roo.util.Observable
25643  * @abstract
25644  * This class is an abstract base class for implementations which provide retrieval of
25645  * unformatted data objects.<br>
25646  * <p>
25647  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25648  * (of the appropriate type which knows how to parse the data object) to provide a block of
25649  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25650  * <p>
25651  * Custom implementations must implement the load method as described in
25652  * {@link Roo.data.HttpProxy#load}.
25653  */
25654 Roo.data.DataProxy = function(){
25655     this.addEvents({
25656         /**
25657          * @event beforeload
25658          * Fires before a network request is made to retrieve a data object.
25659          * @param {Object} This DataProxy object.
25660          * @param {Object} params The params parameter to the load function.
25661          */
25662         beforeload : true,
25663         /**
25664          * @event load
25665          * Fires before the load method's callback is called.
25666          * @param {Object} This DataProxy object.
25667          * @param {Object} o The data object.
25668          * @param {Object} arg The callback argument object passed to the load function.
25669          */
25670         load : true,
25671         /**
25672          * @event loadexception
25673          * Fires if an Exception occurs during data retrieval.
25674          * @param {Object} This DataProxy object.
25675          * @param {Object} o The data object.
25676          * @param {Object} arg The callback argument object passed to the load function.
25677          * @param {Object} e The Exception.
25678          */
25679         loadexception : true
25680     });
25681     Roo.data.DataProxy.superclass.constructor.call(this);
25682 };
25683
25684 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25685
25686     /**
25687      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25688      */
25689 /*
25690  * Based on:
25691  * Ext JS Library 1.1.1
25692  * Copyright(c) 2006-2007, Ext JS, LLC.
25693  *
25694  * Originally Released Under LGPL - original licence link has changed is not relivant.
25695  *
25696  * Fork - LGPL
25697  * <script type="text/javascript">
25698  */
25699 /**
25700  * @class Roo.data.MemoryProxy
25701  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25702  * to the Reader when its load method is called.
25703  * @constructor
25704  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25705  */
25706 Roo.data.MemoryProxy = function(data){
25707     if (data.data) {
25708         data = data.data;
25709     }
25710     Roo.data.MemoryProxy.superclass.constructor.call(this);
25711     this.data = data;
25712 };
25713
25714 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25715     
25716     /**
25717      * Load data from the requested source (in this case an in-memory
25718      * data object passed to the constructor), read the data object into
25719      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25720      * process that block using the passed callback.
25721      * @param {Object} params This parameter is not used by the MemoryProxy class.
25722      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25723      * object into a block of Roo.data.Records.
25724      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25725      * The function must be passed <ul>
25726      * <li>The Record block object</li>
25727      * <li>The "arg" argument from the load function</li>
25728      * <li>A boolean success indicator</li>
25729      * </ul>
25730      * @param {Object} scope The scope in which to call the callback
25731      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25732      */
25733     load : function(params, reader, callback, scope, arg){
25734         params = params || {};
25735         var result;
25736         try {
25737             result = reader.readRecords(params.data ? params.data :this.data);
25738         }catch(e){
25739             this.fireEvent("loadexception", this, arg, null, e);
25740             callback.call(scope, null, arg, false);
25741             return;
25742         }
25743         callback.call(scope, result, arg, true);
25744     },
25745     
25746     // private
25747     update : function(params, records){
25748         
25749     }
25750 });/*
25751  * Based on:
25752  * Ext JS Library 1.1.1
25753  * Copyright(c) 2006-2007, Ext JS, LLC.
25754  *
25755  * Originally Released Under LGPL - original licence link has changed is not relivant.
25756  *
25757  * Fork - LGPL
25758  * <script type="text/javascript">
25759  */
25760 /**
25761  * @class Roo.data.HttpProxy
25762  * @extends Roo.data.DataProxy
25763  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25764  * configured to reference a certain URL.<br><br>
25765  * <p>
25766  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25767  * from which the running page was served.<br><br>
25768  * <p>
25769  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25770  * <p>
25771  * Be aware that to enable the browser to parse an XML document, the server must set
25772  * the Content-Type header in the HTTP response to "text/xml".
25773  * @constructor
25774  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25775  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25776  * will be used to make the request.
25777  */
25778 Roo.data.HttpProxy = function(conn){
25779     Roo.data.HttpProxy.superclass.constructor.call(this);
25780     // is conn a conn config or a real conn?
25781     this.conn = conn;
25782     this.useAjax = !conn || !conn.events;
25783   
25784 };
25785
25786 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25787     // thse are take from connection...
25788     
25789     /**
25790      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25791      */
25792     /**
25793      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25794      * extra parameters to each request made by this object. (defaults to undefined)
25795      */
25796     /**
25797      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25798      *  to each request made by this object. (defaults to undefined)
25799      */
25800     /**
25801      * @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)
25802      */
25803     /**
25804      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25805      */
25806      /**
25807      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25808      * @type Boolean
25809      */
25810   
25811
25812     /**
25813      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25814      * @type Boolean
25815      */
25816     /**
25817      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25818      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25819      * a finer-grained basis than the DataProxy events.
25820      */
25821     getConnection : function(){
25822         return this.useAjax ? Roo.Ajax : this.conn;
25823     },
25824
25825     /**
25826      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25827      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25828      * process that block using the passed callback.
25829      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25830      * for the request to the remote server.
25831      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25832      * object into a block of Roo.data.Records.
25833      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25834      * The function must be passed <ul>
25835      * <li>The Record block object</li>
25836      * <li>The "arg" argument from the load function</li>
25837      * <li>A boolean success indicator</li>
25838      * </ul>
25839      * @param {Object} scope The scope in which to call the callback
25840      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25841      */
25842     load : function(params, reader, callback, scope, arg){
25843         if(this.fireEvent("beforeload", this, params) !== false){
25844             var  o = {
25845                 params : params || {},
25846                 request: {
25847                     callback : callback,
25848                     scope : scope,
25849                     arg : arg
25850                 },
25851                 reader: reader,
25852                 callback : this.loadResponse,
25853                 scope: this
25854             };
25855             if(this.useAjax){
25856                 Roo.applyIf(o, this.conn);
25857                 if(this.activeRequest){
25858                     Roo.Ajax.abort(this.activeRequest);
25859                 }
25860                 this.activeRequest = Roo.Ajax.request(o);
25861             }else{
25862                 this.conn.request(o);
25863             }
25864         }else{
25865             callback.call(scope||this, null, arg, false);
25866         }
25867     },
25868
25869     // private
25870     loadResponse : function(o, success, response){
25871         delete this.activeRequest;
25872         if(!success){
25873             this.fireEvent("loadexception", this, o, response);
25874             o.request.callback.call(o.request.scope, null, o.request.arg, false);
25875             return;
25876         }
25877         var result;
25878         try {
25879             result = o.reader.read(response);
25880         }catch(e){
25881             o.success = false;
25882             o.raw = { errorMsg : response.responseText };
25883             this.fireEvent("loadexception", this, o, response, e);
25884             o.request.callback.call(o.request.scope, o, o.request.arg, false);
25885             return;
25886         }
25887         
25888         this.fireEvent("load", this, o, o.request.arg);
25889         o.request.callback.call(o.request.scope, result, o.request.arg, true);
25890     },
25891
25892     // private
25893     update : function(dataSet){
25894
25895     },
25896
25897     // private
25898     updateResponse : function(dataSet){
25899
25900     }
25901 });/*
25902  * Based on:
25903  * Ext JS Library 1.1.1
25904  * Copyright(c) 2006-2007, Ext JS, LLC.
25905  *
25906  * Originally Released Under LGPL - original licence link has changed is not relivant.
25907  *
25908  * Fork - LGPL
25909  * <script type="text/javascript">
25910  */
25911
25912 /**
25913  * @class Roo.data.ScriptTagProxy
25914  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25915  * other than the originating domain of the running page.<br><br>
25916  * <p>
25917  * <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
25918  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25919  * <p>
25920  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25921  * source code that is used as the source inside a &lt;script> tag.<br><br>
25922  * <p>
25923  * In order for the browser to process the returned data, the server must wrap the data object
25924  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25925  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25926  * depending on whether the callback name was passed:
25927  * <p>
25928  * <pre><code>
25929 boolean scriptTag = false;
25930 String cb = request.getParameter("callback");
25931 if (cb != null) {
25932     scriptTag = true;
25933     response.setContentType("text/javascript");
25934 } else {
25935     response.setContentType("application/x-json");
25936 }
25937 Writer out = response.getWriter();
25938 if (scriptTag) {
25939     out.write(cb + "(");
25940 }
25941 out.print(dataBlock.toJsonString());
25942 if (scriptTag) {
25943     out.write(");");
25944 }
25945 </pre></code>
25946  *
25947  * @constructor
25948  * @param {Object} config A configuration object.
25949  */
25950 Roo.data.ScriptTagProxy = function(config){
25951     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25952     Roo.apply(this, config);
25953     this.head = document.getElementsByTagName("head")[0];
25954 };
25955
25956 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25957
25958 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25959     /**
25960      * @cfg {String} url The URL from which to request the data object.
25961      */
25962     /**
25963      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25964      */
25965     timeout : 30000,
25966     /**
25967      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25968      * the server the name of the callback function set up by the load call to process the returned data object.
25969      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25970      * javascript output which calls this named function passing the data object as its only parameter.
25971      */
25972     callbackParam : "callback",
25973     /**
25974      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25975      * name to the request.
25976      */
25977     nocache : true,
25978
25979     /**
25980      * Load data from the configured URL, read the data object into
25981      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25982      * process that block using the passed callback.
25983      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25984      * for the request to the remote server.
25985      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25986      * object into a block of Roo.data.Records.
25987      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25988      * The function must be passed <ul>
25989      * <li>The Record block object</li>
25990      * <li>The "arg" argument from the load function</li>
25991      * <li>A boolean success indicator</li>
25992      * </ul>
25993      * @param {Object} scope The scope in which to call the callback
25994      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25995      */
25996     load : function(params, reader, callback, scope, arg){
25997         if(this.fireEvent("beforeload", this, params) !== false){
25998
25999             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
26000
26001             var url = this.url;
26002             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
26003             if(this.nocache){
26004                 url += "&_dc=" + (new Date().getTime());
26005             }
26006             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
26007             var trans = {
26008                 id : transId,
26009                 cb : "stcCallback"+transId,
26010                 scriptId : "stcScript"+transId,
26011                 params : params,
26012                 arg : arg,
26013                 url : url,
26014                 callback : callback,
26015                 scope : scope,
26016                 reader : reader
26017             };
26018             var conn = this;
26019
26020             window[trans.cb] = function(o){
26021                 conn.handleResponse(o, trans);
26022             };
26023
26024             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
26025
26026             if(this.autoAbort !== false){
26027                 this.abort();
26028             }
26029
26030             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
26031
26032             var script = document.createElement("script");
26033             script.setAttribute("src", url);
26034             script.setAttribute("type", "text/javascript");
26035             script.setAttribute("id", trans.scriptId);
26036             this.head.appendChild(script);
26037
26038             this.trans = trans;
26039         }else{
26040             callback.call(scope||this, null, arg, false);
26041         }
26042     },
26043
26044     // private
26045     isLoading : function(){
26046         return this.trans ? true : false;
26047     },
26048
26049     /**
26050      * Abort the current server request.
26051      */
26052     abort : function(){
26053         if(this.isLoading()){
26054             this.destroyTrans(this.trans);
26055         }
26056     },
26057
26058     // private
26059     destroyTrans : function(trans, isLoaded){
26060         this.head.removeChild(document.getElementById(trans.scriptId));
26061         clearTimeout(trans.timeoutId);
26062         if(isLoaded){
26063             window[trans.cb] = undefined;
26064             try{
26065                 delete window[trans.cb];
26066             }catch(e){}
26067         }else{
26068             // if hasn't been loaded, wait for load to remove it to prevent script error
26069             window[trans.cb] = function(){
26070                 window[trans.cb] = undefined;
26071                 try{
26072                     delete window[trans.cb];
26073                 }catch(e){}
26074             };
26075         }
26076     },
26077
26078     // private
26079     handleResponse : function(o, trans){
26080         this.trans = false;
26081         this.destroyTrans(trans, true);
26082         var result;
26083         try {
26084             result = trans.reader.readRecords(o);
26085         }catch(e){
26086             this.fireEvent("loadexception", this, o, trans.arg, e);
26087             trans.callback.call(trans.scope||window, null, trans.arg, false);
26088             return;
26089         }
26090         this.fireEvent("load", this, o, trans.arg);
26091         trans.callback.call(trans.scope||window, result, trans.arg, true);
26092     },
26093
26094     // private
26095     handleFailure : function(trans){
26096         this.trans = false;
26097         this.destroyTrans(trans, false);
26098         this.fireEvent("loadexception", this, null, trans.arg);
26099         trans.callback.call(trans.scope||window, null, trans.arg, false);
26100     }
26101 });/*
26102  * Based on:
26103  * Ext JS Library 1.1.1
26104  * Copyright(c) 2006-2007, Ext JS, LLC.
26105  *
26106  * Originally Released Under LGPL - original licence link has changed is not relivant.
26107  *
26108  * Fork - LGPL
26109  * <script type="text/javascript">
26110  */
26111
26112 /**
26113  * @class Roo.data.JsonReader
26114  * @extends Roo.data.DataReader
26115  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
26116  * based on mappings in a provided Roo.data.Record constructor.
26117  * 
26118  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
26119  * in the reply previously. 
26120  * 
26121  * <p>
26122  * Example code:
26123  * <pre><code>
26124 var RecordDef = Roo.data.Record.create([
26125     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26126     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26127 ]);
26128 var myReader = new Roo.data.JsonReader({
26129     totalProperty: "results",    // The property which contains the total dataset size (optional)
26130     root: "rows",                // The property which contains an Array of row objects
26131     id: "id"                     // The property within each row object that provides an ID for the record (optional)
26132 }, RecordDef);
26133 </code></pre>
26134  * <p>
26135  * This would consume a JSON file like this:
26136  * <pre><code>
26137 { 'results': 2, 'rows': [
26138     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
26139     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
26140 }
26141 </code></pre>
26142  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
26143  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26144  * paged from the remote server.
26145  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
26146  * @cfg {String} root name of the property which contains the Array of row objects.
26147  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26148  * @cfg {Array} fields Array of field definition objects
26149  * @constructor
26150  * Create a new JsonReader
26151  * @param {Object} meta Metadata configuration options
26152  * @param {Object} recordType Either an Array of field definition objects,
26153  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
26154  */
26155 Roo.data.JsonReader = function(meta, recordType){
26156     
26157     meta = meta || {};
26158     // set some defaults:
26159     Roo.applyIf(meta, {
26160         totalProperty: 'total',
26161         successProperty : 'success',
26162         root : 'data',
26163         id : 'id'
26164     });
26165     
26166     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26167 };
26168 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
26169     
26170     readerType : 'Json',
26171     
26172     /**
26173      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
26174      * Used by Store query builder to append _requestMeta to params.
26175      * 
26176      */
26177     metaFromRemote : false,
26178     /**
26179      * This method is only used by a DataProxy which has retrieved data from a remote server.
26180      * @param {Object} response The XHR object which contains the JSON data in its responseText.
26181      * @return {Object} data A data block which is used by an Roo.data.Store object as
26182      * a cache of Roo.data.Records.
26183      */
26184     read : function(response){
26185         var json = response.responseText;
26186        
26187         var o = /* eval:var:o */ eval("("+json+")");
26188         if(!o) {
26189             throw {message: "JsonReader.read: Json object not found"};
26190         }
26191         
26192         if(o.metaData){
26193             
26194             delete this.ef;
26195             this.metaFromRemote = true;
26196             this.meta = o.metaData;
26197             this.recordType = Roo.data.Record.create(o.metaData.fields);
26198             this.onMetaChange(this.meta, this.recordType, o);
26199         }
26200         return this.readRecords(o);
26201     },
26202
26203     // private function a store will implement
26204     onMetaChange : function(meta, recordType, o){
26205
26206     },
26207
26208     /**
26209          * @ignore
26210          */
26211     simpleAccess: function(obj, subsc) {
26212         return obj[subsc];
26213     },
26214
26215         /**
26216          * @ignore
26217          */
26218     getJsonAccessor: function(){
26219         var re = /[\[\.]/;
26220         return function(expr) {
26221             try {
26222                 return(re.test(expr))
26223                     ? new Function("obj", "return obj." + expr)
26224                     : function(obj){
26225                         return obj[expr];
26226                     };
26227             } catch(e){}
26228             return Roo.emptyFn;
26229         };
26230     }(),
26231
26232     /**
26233      * Create a data block containing Roo.data.Records from an XML document.
26234      * @param {Object} o An object which contains an Array of row objects in the property specified
26235      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
26236      * which contains the total size of the dataset.
26237      * @return {Object} data A data block which is used by an Roo.data.Store object as
26238      * a cache of Roo.data.Records.
26239      */
26240     readRecords : function(o){
26241         /**
26242          * After any data loads, the raw JSON data is available for further custom processing.
26243          * @type Object
26244          */
26245         this.o = o;
26246         var s = this.meta, Record = this.recordType,
26247             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
26248
26249 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
26250         if (!this.ef) {
26251             if(s.totalProperty) {
26252                     this.getTotal = this.getJsonAccessor(s.totalProperty);
26253                 }
26254                 if(s.successProperty) {
26255                     this.getSuccess = this.getJsonAccessor(s.successProperty);
26256                 }
26257                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
26258                 if (s.id) {
26259                         var g = this.getJsonAccessor(s.id);
26260                         this.getId = function(rec) {
26261                                 var r = g(rec);  
26262                                 return (r === undefined || r === "") ? null : r;
26263                         };
26264                 } else {
26265                         this.getId = function(){return null;};
26266                 }
26267             this.ef = [];
26268             for(var jj = 0; jj < fl; jj++){
26269                 f = fi[jj];
26270                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
26271                 this.ef[jj] = this.getJsonAccessor(map);
26272             }
26273         }
26274
26275         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
26276         if(s.totalProperty){
26277             var vt = parseInt(this.getTotal(o), 10);
26278             if(!isNaN(vt)){
26279                 totalRecords = vt;
26280             }
26281         }
26282         if(s.successProperty){
26283             var vs = this.getSuccess(o);
26284             if(vs === false || vs === 'false'){
26285                 success = false;
26286             }
26287         }
26288         var records = [];
26289         for(var i = 0; i < c; i++){
26290             var n = root[i];
26291             var values = {};
26292             var id = this.getId(n);
26293             for(var j = 0; j < fl; j++){
26294                 f = fi[j];
26295                                 var v = this.ef[j](n);
26296                                 if (!f.convert) {
26297                                         Roo.log('missing convert for ' + f.name);
26298                                         Roo.log(f);
26299                                         continue;
26300                                 }
26301                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26302             }
26303                         if (!Record) {
26304                                 return {
26305                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26306                                         success : false,
26307                                         records : [],
26308                                         totalRecords : 0
26309                                 };
26310                         }
26311             var record = new Record(values, id);
26312             record.json = n;
26313             records[i] = record;
26314         }
26315         return {
26316             raw : o,
26317             success : success,
26318             records : records,
26319             totalRecords : totalRecords
26320         };
26321     },
26322     // used when loading children.. @see loadDataFromChildren
26323     toLoadData: function(rec)
26324     {
26325         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26326         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26327         return { data : data, total : data.length };
26328         
26329     }
26330 });/*
26331  * Based on:
26332  * Ext JS Library 1.1.1
26333  * Copyright(c) 2006-2007, Ext JS, LLC.
26334  *
26335  * Originally Released Under LGPL - original licence link has changed is not relivant.
26336  *
26337  * Fork - LGPL
26338  * <script type="text/javascript">
26339  */
26340
26341 /**
26342  * @class Roo.data.XmlReader
26343  * @extends Roo.data.DataReader
26344  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26345  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26346  * <p>
26347  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26348  * header in the HTTP response must be set to "text/xml".</em>
26349  * <p>
26350  * Example code:
26351  * <pre><code>
26352 var RecordDef = Roo.data.Record.create([
26353    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26354    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26355 ]);
26356 var myReader = new Roo.data.XmlReader({
26357    totalRecords: "results", // The element which contains the total dataset size (optional)
26358    record: "row",           // The repeated element which contains row information
26359    id: "id"                 // The element within the row that provides an ID for the record (optional)
26360 }, RecordDef);
26361 </code></pre>
26362  * <p>
26363  * This would consume an XML file like this:
26364  * <pre><code>
26365 &lt;?xml?>
26366 &lt;dataset>
26367  &lt;results>2&lt;/results>
26368  &lt;row>
26369    &lt;id>1&lt;/id>
26370    &lt;name>Bill&lt;/name>
26371    &lt;occupation>Gardener&lt;/occupation>
26372  &lt;/row>
26373  &lt;row>
26374    &lt;id>2&lt;/id>
26375    &lt;name>Ben&lt;/name>
26376    &lt;occupation>Horticulturalist&lt;/occupation>
26377  &lt;/row>
26378 &lt;/dataset>
26379 </code></pre>
26380  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26381  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26382  * paged from the remote server.
26383  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26384  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26385  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26386  * a record identifier value.
26387  * @constructor
26388  * Create a new XmlReader
26389  * @param {Object} meta Metadata configuration options
26390  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26391  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26392  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26393  */
26394 Roo.data.XmlReader = function(meta, recordType){
26395     meta = meta || {};
26396     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26397 };
26398 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26399     
26400     readerType : 'Xml',
26401     
26402     /**
26403      * This method is only used by a DataProxy which has retrieved data from a remote server.
26404          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26405          * to contain a method called 'responseXML' that returns an XML document object.
26406      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26407      * a cache of Roo.data.Records.
26408      */
26409     read : function(response){
26410         var doc = response.responseXML;
26411         if(!doc) {
26412             throw {message: "XmlReader.read: XML Document not available"};
26413         }
26414         return this.readRecords(doc);
26415     },
26416
26417     /**
26418      * Create a data block containing Roo.data.Records from an XML document.
26419          * @param {Object} doc A parsed XML document.
26420      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26421      * a cache of Roo.data.Records.
26422      */
26423     readRecords : function(doc){
26424         /**
26425          * After any data loads/reads, the raw XML Document is available for further custom processing.
26426          * @type XMLDocument
26427          */
26428         this.xmlData = doc;
26429         var root = doc.documentElement || doc;
26430         var q = Roo.DomQuery;
26431         var recordType = this.recordType, fields = recordType.prototype.fields;
26432         var sid = this.meta.id;
26433         var totalRecords = 0, success = true;
26434         if(this.meta.totalRecords){
26435             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26436         }
26437         
26438         if(this.meta.success){
26439             var sv = q.selectValue(this.meta.success, root, true);
26440             success = sv !== false && sv !== 'false';
26441         }
26442         var records = [];
26443         var ns = q.select(this.meta.record, root);
26444         for(var i = 0, len = ns.length; i < len; i++) {
26445                 var n = ns[i];
26446                 var values = {};
26447                 var id = sid ? q.selectValue(sid, n) : undefined;
26448                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26449                     var f = fields.items[j];
26450                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26451                     v = f.convert(v);
26452                     values[f.name] = v;
26453                 }
26454                 var record = new recordType(values, id);
26455                 record.node = n;
26456                 records[records.length] = record;
26457             }
26458
26459             return {
26460                 success : success,
26461                 records : records,
26462                 totalRecords : totalRecords || records.length
26463             };
26464     }
26465 });/*
26466  * Based on:
26467  * Ext JS Library 1.1.1
26468  * Copyright(c) 2006-2007, Ext JS, LLC.
26469  *
26470  * Originally Released Under LGPL - original licence link has changed is not relivant.
26471  *
26472  * Fork - LGPL
26473  * <script type="text/javascript">
26474  */
26475
26476 /**
26477  * @class Roo.data.ArrayReader
26478  * @extends Roo.data.DataReader
26479  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26480  * Each element of that Array represents a row of data fields. The
26481  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26482  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26483  * <p>
26484  * Example code:.
26485  * <pre><code>
26486 var RecordDef = Roo.data.Record.create([
26487     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26488     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26489 ]);
26490 var myReader = new Roo.data.ArrayReader({
26491     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26492 }, RecordDef);
26493 </code></pre>
26494  * <p>
26495  * This would consume an Array like this:
26496  * <pre><code>
26497 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26498   </code></pre>
26499  
26500  * @constructor
26501  * Create a new JsonReader
26502  * @param {Object} meta Metadata configuration options.
26503  * @param {Object|Array} recordType Either an Array of field definition objects
26504  * 
26505  * @cfg {Array} fields Array of field definition objects
26506  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26507  * as specified to {@link Roo.data.Record#create},
26508  * or an {@link Roo.data.Record} object
26509  *
26510  * 
26511  * created using {@link Roo.data.Record#create}.
26512  */
26513 Roo.data.ArrayReader = function(meta, recordType)
26514 {    
26515     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26516 };
26517
26518 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26519     
26520       /**
26521      * Create a data block containing Roo.data.Records from an XML document.
26522      * @param {Object} o An Array of row objects which represents the dataset.
26523      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26524      * a cache of Roo.data.Records.
26525      */
26526     readRecords : function(o)
26527     {
26528         var sid = this.meta ? this.meta.id : null;
26529         var recordType = this.recordType, fields = recordType.prototype.fields;
26530         var records = [];
26531         var root = o;
26532         for(var i = 0; i < root.length; i++){
26533             var n = root[i];
26534             var values = {};
26535             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26536             for(var j = 0, jlen = fields.length; j < jlen; j++){
26537                 var f = fields.items[j];
26538                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26539                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26540                 v = f.convert(v);
26541                 values[f.name] = v;
26542             }
26543             var record = new recordType(values, id);
26544             record.json = n;
26545             records[records.length] = record;
26546         }
26547         return {
26548             records : records,
26549             totalRecords : records.length
26550         };
26551     },
26552     // used when loading children.. @see loadDataFromChildren
26553     toLoadData: function(rec)
26554     {
26555         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26556         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26557         
26558     }
26559     
26560     
26561 });/*
26562  * Based on:
26563  * Ext JS Library 1.1.1
26564  * Copyright(c) 2006-2007, Ext JS, LLC.
26565  *
26566  * Originally Released Under LGPL - original licence link has changed is not relivant.
26567  *
26568  * Fork - LGPL
26569  * <script type="text/javascript">
26570  */
26571
26572
26573 /**
26574  * @class Roo.data.Tree
26575  * @extends Roo.util.Observable
26576  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26577  * in the tree have most standard DOM functionality.
26578  * @constructor
26579  * @param {Node} root (optional) The root node
26580  */
26581 Roo.data.Tree = function(root){
26582    this.nodeHash = {};
26583    /**
26584     * The root node for this tree
26585     * @type Node
26586     */
26587    this.root = null;
26588    if(root){
26589        this.setRootNode(root);
26590    }
26591    this.addEvents({
26592        /**
26593         * @event append
26594         * Fires when a new child node is appended to a node in this tree.
26595         * @param {Tree} tree The owner tree
26596         * @param {Node} parent The parent node
26597         * @param {Node} node The newly appended node
26598         * @param {Number} index The index of the newly appended node
26599         */
26600        "append" : true,
26601        /**
26602         * @event remove
26603         * Fires when a child node is removed from a node in this tree.
26604         * @param {Tree} tree The owner tree
26605         * @param {Node} parent The parent node
26606         * @param {Node} node The child node removed
26607         */
26608        "remove" : true,
26609        /**
26610         * @event move
26611         * Fires when a node is moved to a new location in the tree
26612         * @param {Tree} tree The owner tree
26613         * @param {Node} node The node moved
26614         * @param {Node} oldParent The old parent of this node
26615         * @param {Node} newParent The new parent of this node
26616         * @param {Number} index The index it was moved to
26617         */
26618        "move" : true,
26619        /**
26620         * @event insert
26621         * Fires when a new child node is inserted in a node in this tree.
26622         * @param {Tree} tree The owner tree
26623         * @param {Node} parent The parent node
26624         * @param {Node} node The child node inserted
26625         * @param {Node} refNode The child node the node was inserted before
26626         */
26627        "insert" : true,
26628        /**
26629         * @event beforeappend
26630         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26631         * @param {Tree} tree The owner tree
26632         * @param {Node} parent The parent node
26633         * @param {Node} node The child node to be appended
26634         */
26635        "beforeappend" : true,
26636        /**
26637         * @event beforeremove
26638         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26639         * @param {Tree} tree The owner tree
26640         * @param {Node} parent The parent node
26641         * @param {Node} node The child node to be removed
26642         */
26643        "beforeremove" : true,
26644        /**
26645         * @event beforemove
26646         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26647         * @param {Tree} tree The owner tree
26648         * @param {Node} node The node being moved
26649         * @param {Node} oldParent The parent of the node
26650         * @param {Node} newParent The new parent the node is moving to
26651         * @param {Number} index The index it is being moved to
26652         */
26653        "beforemove" : true,
26654        /**
26655         * @event beforeinsert
26656         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26657         * @param {Tree} tree The owner tree
26658         * @param {Node} parent The parent node
26659         * @param {Node} node The child node to be inserted
26660         * @param {Node} refNode The child node the node is being inserted before
26661         */
26662        "beforeinsert" : true
26663    });
26664
26665     Roo.data.Tree.superclass.constructor.call(this);
26666 };
26667
26668 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26669     pathSeparator: "/",
26670
26671     proxyNodeEvent : function(){
26672         return this.fireEvent.apply(this, arguments);
26673     },
26674
26675     /**
26676      * Returns the root node for this tree.
26677      * @return {Node}
26678      */
26679     getRootNode : function(){
26680         return this.root;
26681     },
26682
26683     /**
26684      * Sets the root node for this tree.
26685      * @param {Node} node
26686      * @return {Node}
26687      */
26688     setRootNode : function(node){
26689         this.root = node;
26690         node.ownerTree = this;
26691         node.isRoot = true;
26692         this.registerNode(node);
26693         return node;
26694     },
26695
26696     /**
26697      * Gets a node in this tree by its id.
26698      * @param {String} id
26699      * @return {Node}
26700      */
26701     getNodeById : function(id){
26702         return this.nodeHash[id];
26703     },
26704
26705     registerNode : function(node){
26706         this.nodeHash[node.id] = node;
26707     },
26708
26709     unregisterNode : function(node){
26710         delete this.nodeHash[node.id];
26711     },
26712
26713     toString : function(){
26714         return "[Tree"+(this.id?" "+this.id:"")+"]";
26715     }
26716 });
26717
26718 /**
26719  * @class Roo.data.Node
26720  * @extends Roo.util.Observable
26721  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26722  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26723  * @constructor
26724  * @param {Object} attributes The attributes/config for the node
26725  */
26726 Roo.data.Node = function(attributes){
26727     /**
26728      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26729      * @type {Object}
26730      */
26731     this.attributes = attributes || {};
26732     this.leaf = this.attributes.leaf;
26733     /**
26734      * The node id. @type String
26735      */
26736     this.id = this.attributes.id;
26737     if(!this.id){
26738         this.id = Roo.id(null, "ynode-");
26739         this.attributes.id = this.id;
26740     }
26741      
26742     
26743     /**
26744      * All child nodes of this node. @type Array
26745      */
26746     this.childNodes = [];
26747     if(!this.childNodes.indexOf){ // indexOf is a must
26748         this.childNodes.indexOf = function(o){
26749             for(var i = 0, len = this.length; i < len; i++){
26750                 if(this[i] == o) {
26751                     return i;
26752                 }
26753             }
26754             return -1;
26755         };
26756     }
26757     /**
26758      * The parent node for this node. @type Node
26759      */
26760     this.parentNode = null;
26761     /**
26762      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26763      */
26764     this.firstChild = null;
26765     /**
26766      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26767      */
26768     this.lastChild = null;
26769     /**
26770      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26771      */
26772     this.previousSibling = null;
26773     /**
26774      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26775      */
26776     this.nextSibling = null;
26777
26778     this.addEvents({
26779        /**
26780         * @event append
26781         * Fires when a new child node is appended
26782         * @param {Tree} tree The owner tree
26783         * @param {Node} this This node
26784         * @param {Node} node The newly appended node
26785         * @param {Number} index The index of the newly appended node
26786         */
26787        "append" : true,
26788        /**
26789         * @event remove
26790         * Fires when a child node is removed
26791         * @param {Tree} tree The owner tree
26792         * @param {Node} this This node
26793         * @param {Node} node The removed node
26794         */
26795        "remove" : true,
26796        /**
26797         * @event move
26798         * Fires when this node is moved to a new location in the tree
26799         * @param {Tree} tree The owner tree
26800         * @param {Node} this This node
26801         * @param {Node} oldParent The old parent of this node
26802         * @param {Node} newParent The new parent of this node
26803         * @param {Number} index The index it was moved to
26804         */
26805        "move" : true,
26806        /**
26807         * @event insert
26808         * Fires when a new child node is inserted.
26809         * @param {Tree} tree The owner tree
26810         * @param {Node} this This node
26811         * @param {Node} node The child node inserted
26812         * @param {Node} refNode The child node the node was inserted before
26813         */
26814        "insert" : true,
26815        /**
26816         * @event beforeappend
26817         * Fires before a new child is appended, return false to cancel the append.
26818         * @param {Tree} tree The owner tree
26819         * @param {Node} this This node
26820         * @param {Node} node The child node to be appended
26821         */
26822        "beforeappend" : true,
26823        /**
26824         * @event beforeremove
26825         * Fires before a child is removed, return false to cancel the remove.
26826         * @param {Tree} tree The owner tree
26827         * @param {Node} this This node
26828         * @param {Node} node The child node to be removed
26829         */
26830        "beforeremove" : true,
26831        /**
26832         * @event beforemove
26833         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26834         * @param {Tree} tree The owner tree
26835         * @param {Node} this This node
26836         * @param {Node} oldParent The parent of this node
26837         * @param {Node} newParent The new parent this node is moving to
26838         * @param {Number} index The index it is being moved to
26839         */
26840        "beforemove" : true,
26841        /**
26842         * @event beforeinsert
26843         * Fires before a new child is inserted, return false to cancel the insert.
26844         * @param {Tree} tree The owner tree
26845         * @param {Node} this This node
26846         * @param {Node} node The child node to be inserted
26847         * @param {Node} refNode The child node the node is being inserted before
26848         */
26849        "beforeinsert" : true
26850    });
26851     this.listeners = this.attributes.listeners;
26852     Roo.data.Node.superclass.constructor.call(this);
26853 };
26854
26855 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26856     fireEvent : function(evtName){
26857         // first do standard event for this node
26858         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26859             return false;
26860         }
26861         // then bubble it up to the tree if the event wasn't cancelled
26862         var ot = this.getOwnerTree();
26863         if(ot){
26864             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26865                 return false;
26866             }
26867         }
26868         return true;
26869     },
26870
26871     /**
26872      * Returns true if this node is a leaf
26873      * @return {Boolean}
26874      */
26875     isLeaf : function(){
26876         return this.leaf === true;
26877     },
26878
26879     // private
26880     setFirstChild : function(node){
26881         this.firstChild = node;
26882     },
26883
26884     //private
26885     setLastChild : function(node){
26886         this.lastChild = node;
26887     },
26888
26889
26890     /**
26891      * Returns true if this node is the last child of its parent
26892      * @return {Boolean}
26893      */
26894     isLast : function(){
26895        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26896     },
26897
26898     /**
26899      * Returns true if this node is the first child of its parent
26900      * @return {Boolean}
26901      */
26902     isFirst : function(){
26903        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26904     },
26905
26906     hasChildNodes : function(){
26907         return !this.isLeaf() && this.childNodes.length > 0;
26908     },
26909
26910     /**
26911      * Insert node(s) as the last child node of this node.
26912      * @param {Node/Array} node The node or Array of nodes to append
26913      * @return {Node} The appended node if single append, or null if an array was passed
26914      */
26915     appendChild : function(node){
26916         var multi = false;
26917         if(node instanceof Array){
26918             multi = node;
26919         }else if(arguments.length > 1){
26920             multi = arguments;
26921         }
26922         
26923         // if passed an array or multiple args do them one by one
26924         if(multi){
26925             for(var i = 0, len = multi.length; i < len; i++) {
26926                 this.appendChild(multi[i]);
26927             }
26928         }else{
26929             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26930                 return false;
26931             }
26932             var index = this.childNodes.length;
26933             var oldParent = node.parentNode;
26934             // it's a move, make sure we move it cleanly
26935             if(oldParent){
26936                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26937                     return false;
26938                 }
26939                 oldParent.removeChild(node);
26940             }
26941             
26942             index = this.childNodes.length;
26943             if(index == 0){
26944                 this.setFirstChild(node);
26945             }
26946             this.childNodes.push(node);
26947             node.parentNode = this;
26948             var ps = this.childNodes[index-1];
26949             if(ps){
26950                 node.previousSibling = ps;
26951                 ps.nextSibling = node;
26952             }else{
26953                 node.previousSibling = null;
26954             }
26955             node.nextSibling = null;
26956             this.setLastChild(node);
26957             node.setOwnerTree(this.getOwnerTree());
26958             this.fireEvent("append", this.ownerTree, this, node, index);
26959             if(this.ownerTree) {
26960                 this.ownerTree.fireEvent("appendnode", this, node, index);
26961             }
26962             if(oldParent){
26963                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26964             }
26965             return node;
26966         }
26967     },
26968
26969     /**
26970      * Removes a child node from this node.
26971      * @param {Node} node The node to remove
26972      * @return {Node} The removed node
26973      */
26974     removeChild : function(node){
26975         var index = this.childNodes.indexOf(node);
26976         if(index == -1){
26977             return false;
26978         }
26979         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26980             return false;
26981         }
26982
26983         // remove it from childNodes collection
26984         this.childNodes.splice(index, 1);
26985
26986         // update siblings
26987         if(node.previousSibling){
26988             node.previousSibling.nextSibling = node.nextSibling;
26989         }
26990         if(node.nextSibling){
26991             node.nextSibling.previousSibling = node.previousSibling;
26992         }
26993
26994         // update child refs
26995         if(this.firstChild == node){
26996             this.setFirstChild(node.nextSibling);
26997         }
26998         if(this.lastChild == node){
26999             this.setLastChild(node.previousSibling);
27000         }
27001
27002         node.setOwnerTree(null);
27003         // clear any references from the node
27004         node.parentNode = null;
27005         node.previousSibling = null;
27006         node.nextSibling = null;
27007         this.fireEvent("remove", this.ownerTree, this, node);
27008         return node;
27009     },
27010
27011     /**
27012      * Inserts the first node before the second node in this nodes childNodes collection.
27013      * @param {Node} node The node to insert
27014      * @param {Node} refNode The node to insert before (if null the node is appended)
27015      * @return {Node} The inserted node
27016      */
27017     insertBefore : function(node, refNode){
27018         if(!refNode){ // like standard Dom, refNode can be null for append
27019             return this.appendChild(node);
27020         }
27021         // nothing to do
27022         if(node == refNode){
27023             return false;
27024         }
27025
27026         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
27027             return false;
27028         }
27029         var index = this.childNodes.indexOf(refNode);
27030         var oldParent = node.parentNode;
27031         var refIndex = index;
27032
27033         // when moving internally, indexes will change after remove
27034         if(oldParent == this && this.childNodes.indexOf(node) < index){
27035             refIndex--;
27036         }
27037
27038         // it's a move, make sure we move it cleanly
27039         if(oldParent){
27040             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
27041                 return false;
27042             }
27043             oldParent.removeChild(node);
27044         }
27045         if(refIndex == 0){
27046             this.setFirstChild(node);
27047         }
27048         this.childNodes.splice(refIndex, 0, node);
27049         node.parentNode = this;
27050         var ps = this.childNodes[refIndex-1];
27051         if(ps){
27052             node.previousSibling = ps;
27053             ps.nextSibling = node;
27054         }else{
27055             node.previousSibling = null;
27056         }
27057         node.nextSibling = refNode;
27058         refNode.previousSibling = node;
27059         node.setOwnerTree(this.getOwnerTree());
27060         this.fireEvent("insert", this.ownerTree, this, node, refNode);
27061         if(oldParent){
27062             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
27063         }
27064         return node;
27065     },
27066
27067     /**
27068      * Returns the child node at the specified index.
27069      * @param {Number} index
27070      * @return {Node}
27071      */
27072     item : function(index){
27073         return this.childNodes[index];
27074     },
27075
27076     /**
27077      * Replaces one child node in this node with another.
27078      * @param {Node} newChild The replacement node
27079      * @param {Node} oldChild The node to replace
27080      * @return {Node} The replaced node
27081      */
27082     replaceChild : function(newChild, oldChild){
27083         this.insertBefore(newChild, oldChild);
27084         this.removeChild(oldChild);
27085         return oldChild;
27086     },
27087
27088     /**
27089      * Returns the index of a child node
27090      * @param {Node} node
27091      * @return {Number} The index of the node or -1 if it was not found
27092      */
27093     indexOf : function(child){
27094         return this.childNodes.indexOf(child);
27095     },
27096
27097     /**
27098      * Returns the tree this node is in.
27099      * @return {Tree}
27100      */
27101     getOwnerTree : function(){
27102         // if it doesn't have one, look for one
27103         if(!this.ownerTree){
27104             var p = this;
27105             while(p){
27106                 if(p.ownerTree){
27107                     this.ownerTree = p.ownerTree;
27108                     break;
27109                 }
27110                 p = p.parentNode;
27111             }
27112         }
27113         return this.ownerTree;
27114     },
27115
27116     /**
27117      * Returns depth of this node (the root node has a depth of 0)
27118      * @return {Number}
27119      */
27120     getDepth : function(){
27121         var depth = 0;
27122         var p = this;
27123         while(p.parentNode){
27124             ++depth;
27125             p = p.parentNode;
27126         }
27127         return depth;
27128     },
27129
27130     // private
27131     setOwnerTree : function(tree){
27132         // if it's move, we need to update everyone
27133         if(tree != this.ownerTree){
27134             if(this.ownerTree){
27135                 this.ownerTree.unregisterNode(this);
27136             }
27137             this.ownerTree = tree;
27138             var cs = this.childNodes;
27139             for(var i = 0, len = cs.length; i < len; i++) {
27140                 cs[i].setOwnerTree(tree);
27141             }
27142             if(tree){
27143                 tree.registerNode(this);
27144             }
27145         }
27146     },
27147
27148     /**
27149      * Returns the path for this node. The path can be used to expand or select this node programmatically.
27150      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
27151      * @return {String} The path
27152      */
27153     getPath : function(attr){
27154         attr = attr || "id";
27155         var p = this.parentNode;
27156         var b = [this.attributes[attr]];
27157         while(p){
27158             b.unshift(p.attributes[attr]);
27159             p = p.parentNode;
27160         }
27161         var sep = this.getOwnerTree().pathSeparator;
27162         return sep + b.join(sep);
27163     },
27164
27165     /**
27166      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27167      * function call will be the scope provided or the current node. The arguments to the function
27168      * will be the args provided or the current node. If the function returns false at any point,
27169      * the bubble is stopped.
27170      * @param {Function} fn The function to call
27171      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27172      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27173      */
27174     bubble : function(fn, scope, args){
27175         var p = this;
27176         while(p){
27177             if(fn.call(scope || p, args || p) === false){
27178                 break;
27179             }
27180             p = p.parentNode;
27181         }
27182     },
27183
27184     /**
27185      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27186      * function call will be the scope provided or the current node. The arguments to the function
27187      * will be the args provided or the current node. If the function returns false at any point,
27188      * the cascade is stopped on that branch.
27189      * @param {Function} fn The function to call
27190      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27191      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27192      */
27193     cascade : function(fn, scope, args){
27194         if(fn.call(scope || this, args || this) !== false){
27195             var cs = this.childNodes;
27196             for(var i = 0, len = cs.length; i < len; i++) {
27197                 cs[i].cascade(fn, scope, args);
27198             }
27199         }
27200     },
27201
27202     /**
27203      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
27204      * function call will be the scope provided or the current node. The arguments to the function
27205      * will be the args provided or the current node. If the function returns false at any point,
27206      * the iteration stops.
27207      * @param {Function} fn The function to call
27208      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27209      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27210      */
27211     eachChild : function(fn, scope, args){
27212         var cs = this.childNodes;
27213         for(var i = 0, len = cs.length; i < len; i++) {
27214                 if(fn.call(scope || this, args || cs[i]) === false){
27215                     break;
27216                 }
27217         }
27218     },
27219
27220     /**
27221      * Finds the first child that has the attribute with the specified value.
27222      * @param {String} attribute The attribute name
27223      * @param {Mixed} value The value to search for
27224      * @return {Node} The found child or null if none was found
27225      */
27226     findChild : function(attribute, value){
27227         var cs = this.childNodes;
27228         for(var i = 0, len = cs.length; i < len; i++) {
27229                 if(cs[i].attributes[attribute] == value){
27230                     return cs[i];
27231                 }
27232         }
27233         return null;
27234     },
27235
27236     /**
27237      * Finds the first child by a custom function. The child matches if the function passed
27238      * returns true.
27239      * @param {Function} fn
27240      * @param {Object} scope (optional)
27241      * @return {Node} The found child or null if none was found
27242      */
27243     findChildBy : function(fn, scope){
27244         var cs = this.childNodes;
27245         for(var i = 0, len = cs.length; i < len; i++) {
27246                 if(fn.call(scope||cs[i], cs[i]) === true){
27247                     return cs[i];
27248                 }
27249         }
27250         return null;
27251     },
27252
27253     /**
27254      * Sorts this nodes children using the supplied sort function
27255      * @param {Function} fn
27256      * @param {Object} scope (optional)
27257      */
27258     sort : function(fn, scope){
27259         var cs = this.childNodes;
27260         var len = cs.length;
27261         if(len > 0){
27262             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
27263             cs.sort(sortFn);
27264             for(var i = 0; i < len; i++){
27265                 var n = cs[i];
27266                 n.previousSibling = cs[i-1];
27267                 n.nextSibling = cs[i+1];
27268                 if(i == 0){
27269                     this.setFirstChild(n);
27270                 }
27271                 if(i == len-1){
27272                     this.setLastChild(n);
27273                 }
27274             }
27275         }
27276     },
27277
27278     /**
27279      * Returns true if this node is an ancestor (at any point) of the passed node.
27280      * @param {Node} node
27281      * @return {Boolean}
27282      */
27283     contains : function(node){
27284         return node.isAncestor(this);
27285     },
27286
27287     /**
27288      * Returns true if the passed node is an ancestor (at any point) of this node.
27289      * @param {Node} node
27290      * @return {Boolean}
27291      */
27292     isAncestor : function(node){
27293         var p = this.parentNode;
27294         while(p){
27295             if(p == node){
27296                 return true;
27297             }
27298             p = p.parentNode;
27299         }
27300         return false;
27301     },
27302
27303     toString : function(){
27304         return "[Node"+(this.id?" "+this.id:"")+"]";
27305     }
27306 });/*
27307  * Based on:
27308  * Ext JS Library 1.1.1
27309  * Copyright(c) 2006-2007, Ext JS, LLC.
27310  *
27311  * Originally Released Under LGPL - original licence link has changed is not relivant.
27312  *
27313  * Fork - LGPL
27314  * <script type="text/javascript">
27315  */
27316
27317
27318 /**
27319  * @class Roo.Shadow
27320  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
27321  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
27322  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27323  * @constructor
27324  * Create a new Shadow
27325  * @param {Object} config The config object
27326  */
27327 Roo.Shadow = function(config){
27328     Roo.apply(this, config);
27329     if(typeof this.mode != "string"){
27330         this.mode = this.defaultMode;
27331     }
27332     var o = this.offset, a = {h: 0};
27333     var rad = Math.floor(this.offset/2);
27334     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27335         case "drop":
27336             a.w = 0;
27337             a.l = a.t = o;
27338             a.t -= 1;
27339             if(Roo.isIE){
27340                 a.l -= this.offset + rad;
27341                 a.t -= this.offset + rad;
27342                 a.w -= rad;
27343                 a.h -= rad;
27344                 a.t += 1;
27345             }
27346         break;
27347         case "sides":
27348             a.w = (o*2);
27349             a.l = -o;
27350             a.t = o-1;
27351             if(Roo.isIE){
27352                 a.l -= (this.offset - rad);
27353                 a.t -= this.offset + rad;
27354                 a.l += 1;
27355                 a.w -= (this.offset - rad)*2;
27356                 a.w -= rad + 1;
27357                 a.h -= 1;
27358             }
27359         break;
27360         case "frame":
27361             a.w = a.h = (o*2);
27362             a.l = a.t = -o;
27363             a.t += 1;
27364             a.h -= 2;
27365             if(Roo.isIE){
27366                 a.l -= (this.offset - rad);
27367                 a.t -= (this.offset - rad);
27368                 a.l += 1;
27369                 a.w -= (this.offset + rad + 1);
27370                 a.h -= (this.offset + rad);
27371                 a.h += 1;
27372             }
27373         break;
27374     };
27375
27376     this.adjusts = a;
27377 };
27378
27379 Roo.Shadow.prototype = {
27380     /**
27381      * @cfg {String} mode
27382      * The shadow display mode.  Supports the following options:<br />
27383      * sides: Shadow displays on both sides and bottom only<br />
27384      * frame: Shadow displays equally on all four sides<br />
27385      * drop: Traditional bottom-right drop shadow (default)
27386      */
27387     mode: false,
27388     /**
27389      * @cfg {String} offset
27390      * The number of pixels to offset the shadow from the element (defaults to 4)
27391      */
27392     offset: 4,
27393
27394     // private
27395     defaultMode: "drop",
27396
27397     /**
27398      * Displays the shadow under the target element
27399      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27400      */
27401     show : function(target){
27402         target = Roo.get(target);
27403         if(!this.el){
27404             this.el = Roo.Shadow.Pool.pull();
27405             if(this.el.dom.nextSibling != target.dom){
27406                 this.el.insertBefore(target);
27407             }
27408         }
27409         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27410         if(Roo.isIE){
27411             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27412         }
27413         this.realign(
27414             target.getLeft(true),
27415             target.getTop(true),
27416             target.getWidth(),
27417             target.getHeight()
27418         );
27419         this.el.dom.style.display = "block";
27420     },
27421
27422     /**
27423      * Returns true if the shadow is visible, else false
27424      */
27425     isVisible : function(){
27426         return this.el ? true : false;  
27427     },
27428
27429     /**
27430      * Direct alignment when values are already available. Show must be called at least once before
27431      * calling this method to ensure it is initialized.
27432      * @param {Number} left The target element left position
27433      * @param {Number} top The target element top position
27434      * @param {Number} width The target element width
27435      * @param {Number} height The target element height
27436      */
27437     realign : function(l, t, w, h){
27438         if(!this.el){
27439             return;
27440         }
27441         var a = this.adjusts, d = this.el.dom, s = d.style;
27442         var iea = 0;
27443         s.left = (l+a.l)+"px";
27444         s.top = (t+a.t)+"px";
27445         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27446  
27447         if(s.width != sws || s.height != shs){
27448             s.width = sws;
27449             s.height = shs;
27450             if(!Roo.isIE){
27451                 var cn = d.childNodes;
27452                 var sww = Math.max(0, (sw-12))+"px";
27453                 cn[0].childNodes[1].style.width = sww;
27454                 cn[1].childNodes[1].style.width = sww;
27455                 cn[2].childNodes[1].style.width = sww;
27456                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27457             }
27458         }
27459     },
27460
27461     /**
27462      * Hides this shadow
27463      */
27464     hide : function(){
27465         if(this.el){
27466             this.el.dom.style.display = "none";
27467             Roo.Shadow.Pool.push(this.el);
27468             delete this.el;
27469         }
27470     },
27471
27472     /**
27473      * Adjust the z-index of this shadow
27474      * @param {Number} zindex The new z-index
27475      */
27476     setZIndex : function(z){
27477         this.zIndex = z;
27478         if(this.el){
27479             this.el.setStyle("z-index", z);
27480         }
27481     }
27482 };
27483
27484 // Private utility class that manages the internal Shadow cache
27485 Roo.Shadow.Pool = function(){
27486     var p = [];
27487     var markup = Roo.isIE ?
27488                  '<div class="x-ie-shadow"></div>' :
27489                  '<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>';
27490     return {
27491         pull : function(){
27492             var sh = p.shift();
27493             if(!sh){
27494                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27495                 sh.autoBoxAdjust = false;
27496             }
27497             return sh;
27498         },
27499
27500         push : function(sh){
27501             p.push(sh);
27502         }
27503     };
27504 }();/*
27505  * Based on:
27506  * Ext JS Library 1.1.1
27507  * Copyright(c) 2006-2007, Ext JS, LLC.
27508  *
27509  * Originally Released Under LGPL - original licence link has changed is not relivant.
27510  *
27511  * Fork - LGPL
27512  * <script type="text/javascript">
27513  */
27514
27515
27516 /**
27517  * @class Roo.SplitBar
27518  * @extends Roo.util.Observable
27519  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27520  * <br><br>
27521  * Usage:
27522  * <pre><code>
27523 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27524                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27525 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27526 split.minSize = 100;
27527 split.maxSize = 600;
27528 split.animate = true;
27529 split.on('moved', splitterMoved);
27530 </code></pre>
27531  * @constructor
27532  * Create a new SplitBar
27533  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27534  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27535  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27536  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27537                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27538                         position of the SplitBar).
27539  */
27540 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27541     
27542     /** @private */
27543     this.el = Roo.get(dragElement, true);
27544     this.el.dom.unselectable = "on";
27545     /** @private */
27546     this.resizingEl = Roo.get(resizingElement, true);
27547
27548     /**
27549      * @private
27550      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27551      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27552      * @type Number
27553      */
27554     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27555     
27556     /**
27557      * The minimum size of the resizing element. (Defaults to 0)
27558      * @type Number
27559      */
27560     this.minSize = 0;
27561     
27562     /**
27563      * The maximum size of the resizing element. (Defaults to 2000)
27564      * @type Number
27565      */
27566     this.maxSize = 2000;
27567     
27568     /**
27569      * Whether to animate the transition to the new size
27570      * @type Boolean
27571      */
27572     this.animate = false;
27573     
27574     /**
27575      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27576      * @type Boolean
27577      */
27578     this.useShim = false;
27579     
27580     /** @private */
27581     this.shim = null;
27582     
27583     if(!existingProxy){
27584         /** @private */
27585         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27586     }else{
27587         this.proxy = Roo.get(existingProxy).dom;
27588     }
27589     /** @private */
27590     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27591     
27592     /** @private */
27593     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27594     
27595     /** @private */
27596     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27597     
27598     /** @private */
27599     this.dragSpecs = {};
27600     
27601     /**
27602      * @private The adapter to use to positon and resize elements
27603      */
27604     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27605     this.adapter.init(this);
27606     
27607     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27608         /** @private */
27609         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27610         this.el.addClass("x-splitbar-h");
27611     }else{
27612         /** @private */
27613         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27614         this.el.addClass("x-splitbar-v");
27615     }
27616     
27617     this.addEvents({
27618         /**
27619          * @event resize
27620          * Fires when the splitter is moved (alias for {@link #event-moved})
27621          * @param {Roo.SplitBar} this
27622          * @param {Number} newSize the new width or height
27623          */
27624         "resize" : true,
27625         /**
27626          * @event moved
27627          * Fires when the splitter is moved
27628          * @param {Roo.SplitBar} this
27629          * @param {Number} newSize the new width or height
27630          */
27631         "moved" : true,
27632         /**
27633          * @event beforeresize
27634          * Fires before the splitter is dragged
27635          * @param {Roo.SplitBar} this
27636          */
27637         "beforeresize" : true,
27638
27639         "beforeapply" : true
27640     });
27641
27642     Roo.util.Observable.call(this);
27643 };
27644
27645 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27646     onStartProxyDrag : function(x, y){
27647         this.fireEvent("beforeresize", this);
27648         if(!this.overlay){
27649             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27650             o.unselectable();
27651             o.enableDisplayMode("block");
27652             // all splitbars share the same overlay
27653             Roo.SplitBar.prototype.overlay = o;
27654         }
27655         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27656         this.overlay.show();
27657         Roo.get(this.proxy).setDisplayed("block");
27658         var size = this.adapter.getElementSize(this);
27659         this.activeMinSize = this.getMinimumSize();;
27660         this.activeMaxSize = this.getMaximumSize();;
27661         var c1 = size - this.activeMinSize;
27662         var c2 = Math.max(this.activeMaxSize - size, 0);
27663         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27664             this.dd.resetConstraints();
27665             this.dd.setXConstraint(
27666                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27667                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27668             );
27669             this.dd.setYConstraint(0, 0);
27670         }else{
27671             this.dd.resetConstraints();
27672             this.dd.setXConstraint(0, 0);
27673             this.dd.setYConstraint(
27674                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27675                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27676             );
27677          }
27678         this.dragSpecs.startSize = size;
27679         this.dragSpecs.startPoint = [x, y];
27680         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27681     },
27682     
27683     /** 
27684      * @private Called after the drag operation by the DDProxy
27685      */
27686     onEndProxyDrag : function(e){
27687         Roo.get(this.proxy).setDisplayed(false);
27688         var endPoint = Roo.lib.Event.getXY(e);
27689         if(this.overlay){
27690             this.overlay.hide();
27691         }
27692         var newSize;
27693         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27694             newSize = this.dragSpecs.startSize + 
27695                 (this.placement == Roo.SplitBar.LEFT ?
27696                     endPoint[0] - this.dragSpecs.startPoint[0] :
27697                     this.dragSpecs.startPoint[0] - endPoint[0]
27698                 );
27699         }else{
27700             newSize = this.dragSpecs.startSize + 
27701                 (this.placement == Roo.SplitBar.TOP ?
27702                     endPoint[1] - this.dragSpecs.startPoint[1] :
27703                     this.dragSpecs.startPoint[1] - endPoint[1]
27704                 );
27705         }
27706         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27707         if(newSize != this.dragSpecs.startSize){
27708             if(this.fireEvent('beforeapply', this, newSize) !== false){
27709                 this.adapter.setElementSize(this, newSize);
27710                 this.fireEvent("moved", this, newSize);
27711                 this.fireEvent("resize", this, newSize);
27712             }
27713         }
27714     },
27715     
27716     /**
27717      * Get the adapter this SplitBar uses
27718      * @return The adapter object
27719      */
27720     getAdapter : function(){
27721         return this.adapter;
27722     },
27723     
27724     /**
27725      * Set the adapter this SplitBar uses
27726      * @param {Object} adapter A SplitBar adapter object
27727      */
27728     setAdapter : function(adapter){
27729         this.adapter = adapter;
27730         this.adapter.init(this);
27731     },
27732     
27733     /**
27734      * Gets the minimum size for the resizing element
27735      * @return {Number} The minimum size
27736      */
27737     getMinimumSize : function(){
27738         return this.minSize;
27739     },
27740     
27741     /**
27742      * Sets the minimum size for the resizing element
27743      * @param {Number} minSize The minimum size
27744      */
27745     setMinimumSize : function(minSize){
27746         this.minSize = minSize;
27747     },
27748     
27749     /**
27750      * Gets the maximum size for the resizing element
27751      * @return {Number} The maximum size
27752      */
27753     getMaximumSize : function(){
27754         return this.maxSize;
27755     },
27756     
27757     /**
27758      * Sets the maximum size for the resizing element
27759      * @param {Number} maxSize The maximum size
27760      */
27761     setMaximumSize : function(maxSize){
27762         this.maxSize = maxSize;
27763     },
27764     
27765     /**
27766      * Sets the initialize size for the resizing element
27767      * @param {Number} size The initial size
27768      */
27769     setCurrentSize : function(size){
27770         var oldAnimate = this.animate;
27771         this.animate = false;
27772         this.adapter.setElementSize(this, size);
27773         this.animate = oldAnimate;
27774     },
27775     
27776     /**
27777      * Destroy this splitbar. 
27778      * @param {Boolean} removeEl True to remove the element
27779      */
27780     destroy : function(removeEl){
27781         if(this.shim){
27782             this.shim.remove();
27783         }
27784         this.dd.unreg();
27785         this.proxy.parentNode.removeChild(this.proxy);
27786         if(removeEl){
27787             this.el.remove();
27788         }
27789     }
27790 });
27791
27792 /**
27793  * @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.
27794  */
27795 Roo.SplitBar.createProxy = function(dir){
27796     var proxy = new Roo.Element(document.createElement("div"));
27797     proxy.unselectable();
27798     var cls = 'x-splitbar-proxy';
27799     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27800     document.body.appendChild(proxy.dom);
27801     return proxy.dom;
27802 };
27803
27804 /** 
27805  * @class Roo.SplitBar.BasicLayoutAdapter
27806  * Default Adapter. It assumes the splitter and resizing element are not positioned
27807  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27808  */
27809 Roo.SplitBar.BasicLayoutAdapter = function(){
27810 };
27811
27812 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27813     // do nothing for now
27814     init : function(s){
27815     
27816     },
27817     /**
27818      * Called before drag operations to get the current size of the resizing element. 
27819      * @param {Roo.SplitBar} s The SplitBar using this adapter
27820      */
27821      getElementSize : function(s){
27822         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27823             return s.resizingEl.getWidth();
27824         }else{
27825             return s.resizingEl.getHeight();
27826         }
27827     },
27828     
27829     /**
27830      * Called after drag operations to set the size of the resizing element.
27831      * @param {Roo.SplitBar} s The SplitBar using this adapter
27832      * @param {Number} newSize The new size to set
27833      * @param {Function} onComplete A function to be invoked when resizing is complete
27834      */
27835     setElementSize : function(s, newSize, onComplete){
27836         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27837             if(!s.animate){
27838                 s.resizingEl.setWidth(newSize);
27839                 if(onComplete){
27840                     onComplete(s, newSize);
27841                 }
27842             }else{
27843                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27844             }
27845         }else{
27846             
27847             if(!s.animate){
27848                 s.resizingEl.setHeight(newSize);
27849                 if(onComplete){
27850                     onComplete(s, newSize);
27851                 }
27852             }else{
27853                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27854             }
27855         }
27856     }
27857 };
27858
27859 /** 
27860  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27861  * @extends Roo.SplitBar.BasicLayoutAdapter
27862  * Adapter that  moves the splitter element to align with the resized sizing element. 
27863  * Used with an absolute positioned SplitBar.
27864  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27865  * document.body, make sure you assign an id to the body element.
27866  */
27867 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27868     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27869     this.container = Roo.get(container);
27870 };
27871
27872 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27873     init : function(s){
27874         this.basic.init(s);
27875     },
27876     
27877     getElementSize : function(s){
27878         return this.basic.getElementSize(s);
27879     },
27880     
27881     setElementSize : function(s, newSize, onComplete){
27882         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27883     },
27884     
27885     moveSplitter : function(s){
27886         var yes = Roo.SplitBar;
27887         switch(s.placement){
27888             case yes.LEFT:
27889                 s.el.setX(s.resizingEl.getRight());
27890                 break;
27891             case yes.RIGHT:
27892                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27893                 break;
27894             case yes.TOP:
27895                 s.el.setY(s.resizingEl.getBottom());
27896                 break;
27897             case yes.BOTTOM:
27898                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27899                 break;
27900         }
27901     }
27902 };
27903
27904 /**
27905  * Orientation constant - Create a vertical SplitBar
27906  * @static
27907  * @type Number
27908  */
27909 Roo.SplitBar.VERTICAL = 1;
27910
27911 /**
27912  * Orientation constant - Create a horizontal SplitBar
27913  * @static
27914  * @type Number
27915  */
27916 Roo.SplitBar.HORIZONTAL = 2;
27917
27918 /**
27919  * Placement constant - The resizing element is to the left of the splitter element
27920  * @static
27921  * @type Number
27922  */
27923 Roo.SplitBar.LEFT = 1;
27924
27925 /**
27926  * Placement constant - The resizing element is to the right of the splitter element
27927  * @static
27928  * @type Number
27929  */
27930 Roo.SplitBar.RIGHT = 2;
27931
27932 /**
27933  * Placement constant - The resizing element is positioned above the splitter element
27934  * @static
27935  * @type Number
27936  */
27937 Roo.SplitBar.TOP = 3;
27938
27939 /**
27940  * Placement constant - The resizing element is positioned under splitter element
27941  * @static
27942  * @type Number
27943  */
27944 Roo.SplitBar.BOTTOM = 4;
27945 /*
27946  * Based on:
27947  * Ext JS Library 1.1.1
27948  * Copyright(c) 2006-2007, Ext JS, LLC.
27949  *
27950  * Originally Released Under LGPL - original licence link has changed is not relivant.
27951  *
27952  * Fork - LGPL
27953  * <script type="text/javascript">
27954  */
27955
27956 /**
27957  * @class Roo.View
27958  * @extends Roo.util.Observable
27959  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27960  * This class also supports single and multi selection modes. <br>
27961  * Create a data model bound view:
27962  <pre><code>
27963  var store = new Roo.data.Store(...);
27964
27965  var view = new Roo.View({
27966     el : "my-element",
27967     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27968  
27969     singleSelect: true,
27970     selectedClass: "ydataview-selected",
27971     store: store
27972  });
27973
27974  // listen for node click?
27975  view.on("click", function(vw, index, node, e){
27976  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27977  });
27978
27979  // load XML data
27980  dataModel.load("foobar.xml");
27981  </code></pre>
27982  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27983  * <br><br>
27984  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27985  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27986  * 
27987  * Note: old style constructor is still suported (container, template, config)
27988  * 
27989  * @constructor
27990  * Create a new View
27991  * @param {Object} config The config object
27992  * 
27993  */
27994 Roo.View = function(config, depreciated_tpl, depreciated_config){
27995     
27996     this.parent = false;
27997     
27998     if (typeof(depreciated_tpl) == 'undefined') {
27999         // new way.. - universal constructor.
28000         Roo.apply(this, config);
28001         this.el  = Roo.get(this.el);
28002     } else {
28003         // old format..
28004         this.el  = Roo.get(config);
28005         this.tpl = depreciated_tpl;
28006         Roo.apply(this, depreciated_config);
28007     }
28008     this.wrapEl  = this.el.wrap().wrap();
28009     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
28010     
28011     
28012     if(typeof(this.tpl) == "string"){
28013         this.tpl = new Roo.Template(this.tpl);
28014     } else {
28015         // support xtype ctors..
28016         this.tpl = new Roo.factory(this.tpl, Roo);
28017     }
28018     
28019     
28020     this.tpl.compile();
28021     
28022     /** @private */
28023     this.addEvents({
28024         /**
28025          * @event beforeclick
28026          * Fires before a click is processed. Returns false to cancel the default action.
28027          * @param {Roo.View} this
28028          * @param {Number} index The index of the target node
28029          * @param {HTMLElement} node The target node
28030          * @param {Roo.EventObject} e The raw event object
28031          */
28032             "beforeclick" : true,
28033         /**
28034          * @event click
28035          * Fires when a template node is clicked.
28036          * @param {Roo.View} this
28037          * @param {Number} index The index of the target node
28038          * @param {HTMLElement} node The target node
28039          * @param {Roo.EventObject} e The raw event object
28040          */
28041             "click" : true,
28042         /**
28043          * @event dblclick
28044          * Fires when a template node is double clicked.
28045          * @param {Roo.View} this
28046          * @param {Number} index The index of the target node
28047          * @param {HTMLElement} node The target node
28048          * @param {Roo.EventObject} e The raw event object
28049          */
28050             "dblclick" : true,
28051         /**
28052          * @event contextmenu
28053          * Fires when a template node is right clicked.
28054          * @param {Roo.View} this
28055          * @param {Number} index The index of the target node
28056          * @param {HTMLElement} node The target node
28057          * @param {Roo.EventObject} e The raw event object
28058          */
28059             "contextmenu" : true,
28060         /**
28061          * @event selectionchange
28062          * Fires when the selected nodes change.
28063          * @param {Roo.View} this
28064          * @param {Array} selections Array of the selected nodes
28065          */
28066             "selectionchange" : true,
28067     
28068         /**
28069          * @event beforeselect
28070          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
28071          * @param {Roo.View} this
28072          * @param {HTMLElement} node The node to be selected
28073          * @param {Array} selections Array of currently selected nodes
28074          */
28075             "beforeselect" : true,
28076         /**
28077          * @event preparedata
28078          * Fires on every row to render, to allow you to change the data.
28079          * @param {Roo.View} this
28080          * @param {Object} data to be rendered (change this)
28081          */
28082           "preparedata" : true
28083           
28084           
28085         });
28086
28087
28088
28089     this.el.on({
28090         "click": this.onClick,
28091         "dblclick": this.onDblClick,
28092         "contextmenu": this.onContextMenu,
28093         scope:this
28094     });
28095
28096     this.selections = [];
28097     this.nodes = [];
28098     this.cmp = new Roo.CompositeElementLite([]);
28099     if(this.store){
28100         this.store = Roo.factory(this.store, Roo.data);
28101         this.setStore(this.store, true);
28102     }
28103     
28104     if ( this.footer && this.footer.xtype) {
28105            
28106          var fctr = this.wrapEl.appendChild(document.createElement("div"));
28107         
28108         this.footer.dataSource = this.store;
28109         this.footer.container = fctr;
28110         this.footer = Roo.factory(this.footer, Roo);
28111         fctr.insertFirst(this.el);
28112         
28113         // this is a bit insane - as the paging toolbar seems to detach the el..
28114 //        dom.parentNode.parentNode.parentNode
28115          // they get detached?
28116     }
28117     
28118     
28119     Roo.View.superclass.constructor.call(this);
28120     
28121     
28122 };
28123
28124 Roo.extend(Roo.View, Roo.util.Observable, {
28125     
28126      /**
28127      * @cfg {Roo.data.Store} store Data store to load data from.
28128      */
28129     store : false,
28130     
28131     /**
28132      * @cfg {String|Roo.Element} el The container element.
28133      */
28134     el : '',
28135     
28136     /**
28137      * @cfg {String|Roo.Template} tpl The template used by this View 
28138      */
28139     tpl : false,
28140     /**
28141      * @cfg {String} dataName the named area of the template to use as the data area
28142      *                          Works with domtemplates roo-name="name"
28143      */
28144     dataName: false,
28145     /**
28146      * @cfg {String} selectedClass The css class to add to selected nodes
28147      */
28148     selectedClass : "x-view-selected",
28149      /**
28150      * @cfg {String} emptyText The empty text to show when nothing is loaded.
28151      */
28152     emptyText : "",
28153     
28154     /**
28155      * @cfg {String} text to display on mask (default Loading)
28156      */
28157     mask : false,
28158     /**
28159      * @cfg {Boolean} multiSelect Allow multiple selection
28160      */
28161     multiSelect : false,
28162     /**
28163      * @cfg {Boolean} singleSelect Allow single selection
28164      */
28165     singleSelect:  false,
28166     
28167     /**
28168      * @cfg {Boolean} toggleSelect - selecting 
28169      */
28170     toggleSelect : false,
28171     
28172     /**
28173      * @cfg {Boolean} tickable - selecting 
28174      */
28175     tickable : false,
28176     
28177     /**
28178      * Returns the element this view is bound to.
28179      * @return {Roo.Element}
28180      */
28181     getEl : function(){
28182         return this.wrapEl;
28183     },
28184     
28185     
28186
28187     /**
28188      * Refreshes the view. - called by datachanged on the store. - do not call directly.
28189      */
28190     refresh : function(){
28191         //Roo.log('refresh');
28192         var t = this.tpl;
28193         
28194         // if we are using something like 'domtemplate', then
28195         // the what gets used is:
28196         // t.applySubtemplate(NAME, data, wrapping data..)
28197         // the outer template then get' applied with
28198         //     the store 'extra data'
28199         // and the body get's added to the
28200         //      roo-name="data" node?
28201         //      <span class='roo-tpl-{name}'></span> ?????
28202         
28203         
28204         
28205         this.clearSelections();
28206         this.el.update("");
28207         var html = [];
28208         var records = this.store.getRange();
28209         if(records.length < 1) {
28210             
28211             // is this valid??  = should it render a template??
28212             
28213             this.el.update(this.emptyText);
28214             return;
28215         }
28216         var el = this.el;
28217         if (this.dataName) {
28218             this.el.update(t.apply(this.store.meta)); //????
28219             el = this.el.child('.roo-tpl-' + this.dataName);
28220         }
28221         
28222         for(var i = 0, len = records.length; i < len; i++){
28223             var data = this.prepareData(records[i].data, i, records[i]);
28224             this.fireEvent("preparedata", this, data, i, records[i]);
28225             
28226             var d = Roo.apply({}, data);
28227             
28228             if(this.tickable){
28229                 Roo.apply(d, {'roo-id' : Roo.id()});
28230                 
28231                 var _this = this;
28232             
28233                 Roo.each(this.parent.item, function(item){
28234                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
28235                         return;
28236                     }
28237                     Roo.apply(d, {'roo-data-checked' : 'checked'});
28238                 });
28239             }
28240             
28241             html[html.length] = Roo.util.Format.trim(
28242                 this.dataName ?
28243                     t.applySubtemplate(this.dataName, d, this.store.meta) :
28244                     t.apply(d)
28245             );
28246         }
28247         
28248         
28249         
28250         el.update(html.join(""));
28251         this.nodes = el.dom.childNodes;
28252         this.updateIndexes(0);
28253     },
28254     
28255
28256     /**
28257      * Function to override to reformat the data that is sent to
28258      * the template for each node.
28259      * DEPRICATED - use the preparedata event handler.
28260      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
28261      * a JSON object for an UpdateManager bound view).
28262      */
28263     prepareData : function(data, index, record)
28264     {
28265         this.fireEvent("preparedata", this, data, index, record);
28266         return data;
28267     },
28268
28269     onUpdate : function(ds, record){
28270         // Roo.log('on update');   
28271         this.clearSelections();
28272         var index = this.store.indexOf(record);
28273         var n = this.nodes[index];
28274         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
28275         n.parentNode.removeChild(n);
28276         this.updateIndexes(index, index);
28277     },
28278
28279     
28280     
28281 // --------- FIXME     
28282     onAdd : function(ds, records, index)
28283     {
28284         //Roo.log(['on Add', ds, records, index] );        
28285         this.clearSelections();
28286         if(this.nodes.length == 0){
28287             this.refresh();
28288             return;
28289         }
28290         var n = this.nodes[index];
28291         for(var i = 0, len = records.length; i < len; i++){
28292             var d = this.prepareData(records[i].data, i, records[i]);
28293             if(n){
28294                 this.tpl.insertBefore(n, d);
28295             }else{
28296                 
28297                 this.tpl.append(this.el, d);
28298             }
28299         }
28300         this.updateIndexes(index);
28301     },
28302
28303     onRemove : function(ds, record, index){
28304        // Roo.log('onRemove');
28305         this.clearSelections();
28306         var el = this.dataName  ?
28307             this.el.child('.roo-tpl-' + this.dataName) :
28308             this.el; 
28309         
28310         el.dom.removeChild(this.nodes[index]);
28311         this.updateIndexes(index);
28312     },
28313
28314     /**
28315      * Refresh an individual node.
28316      * @param {Number} index
28317      */
28318     refreshNode : function(index){
28319         this.onUpdate(this.store, this.store.getAt(index));
28320     },
28321
28322     updateIndexes : function(startIndex, endIndex){
28323         var ns = this.nodes;
28324         startIndex = startIndex || 0;
28325         endIndex = endIndex || ns.length - 1;
28326         for(var i = startIndex; i <= endIndex; i++){
28327             ns[i].nodeIndex = i;
28328         }
28329     },
28330
28331     /**
28332      * Changes the data store this view uses and refresh the view.
28333      * @param {Store} store
28334      */
28335     setStore : function(store, initial){
28336         if(!initial && this.store){
28337             this.store.un("datachanged", this.refresh);
28338             this.store.un("add", this.onAdd);
28339             this.store.un("remove", this.onRemove);
28340             this.store.un("update", this.onUpdate);
28341             this.store.un("clear", this.refresh);
28342             this.store.un("beforeload", this.onBeforeLoad);
28343             this.store.un("load", this.onLoad);
28344             this.store.un("loadexception", this.onLoad);
28345         }
28346         if(store){
28347           
28348             store.on("datachanged", this.refresh, this);
28349             store.on("add", this.onAdd, this);
28350             store.on("remove", this.onRemove, this);
28351             store.on("update", this.onUpdate, this);
28352             store.on("clear", this.refresh, this);
28353             store.on("beforeload", this.onBeforeLoad, this);
28354             store.on("load", this.onLoad, this);
28355             store.on("loadexception", this.onLoad, this);
28356         }
28357         
28358         if(store){
28359             this.refresh();
28360         }
28361     },
28362     /**
28363      * onbeforeLoad - masks the loading area.
28364      *
28365      */
28366     onBeforeLoad : function(store,opts)
28367     {
28368          //Roo.log('onBeforeLoad');   
28369         if (!opts.add) {
28370             this.el.update("");
28371         }
28372         this.el.mask(this.mask ? this.mask : "Loading" ); 
28373     },
28374     onLoad : function ()
28375     {
28376         this.el.unmask();
28377     },
28378     
28379
28380     /**
28381      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28382      * @param {HTMLElement} node
28383      * @return {HTMLElement} The template node
28384      */
28385     findItemFromChild : function(node){
28386         var el = this.dataName  ?
28387             this.el.child('.roo-tpl-' + this.dataName,true) :
28388             this.el.dom; 
28389         
28390         if(!node || node.parentNode == el){
28391                     return node;
28392             }
28393             var p = node.parentNode;
28394             while(p && p != el){
28395             if(p.parentNode == el){
28396                 return p;
28397             }
28398             p = p.parentNode;
28399         }
28400             return null;
28401     },
28402
28403     /** @ignore */
28404     onClick : function(e){
28405         var item = this.findItemFromChild(e.getTarget());
28406         if(item){
28407             var index = this.indexOf(item);
28408             if(this.onItemClick(item, index, e) !== false){
28409                 this.fireEvent("click", this, index, item, e);
28410             }
28411         }else{
28412             this.clearSelections();
28413         }
28414     },
28415
28416     /** @ignore */
28417     onContextMenu : function(e){
28418         var item = this.findItemFromChild(e.getTarget());
28419         if(item){
28420             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28421         }
28422     },
28423
28424     /** @ignore */
28425     onDblClick : function(e){
28426         var item = this.findItemFromChild(e.getTarget());
28427         if(item){
28428             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28429         }
28430     },
28431
28432     onItemClick : function(item, index, e)
28433     {
28434         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28435             return false;
28436         }
28437         if (this.toggleSelect) {
28438             var m = this.isSelected(item) ? 'unselect' : 'select';
28439             //Roo.log(m);
28440             var _t = this;
28441             _t[m](item, true, false);
28442             return true;
28443         }
28444         if(this.multiSelect || this.singleSelect){
28445             if(this.multiSelect && e.shiftKey && this.lastSelection){
28446                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28447             }else{
28448                 this.select(item, this.multiSelect && e.ctrlKey);
28449                 this.lastSelection = item;
28450             }
28451             
28452             if(!this.tickable){
28453                 e.preventDefault();
28454             }
28455             
28456         }
28457         return true;
28458     },
28459
28460     /**
28461      * Get the number of selected nodes.
28462      * @return {Number}
28463      */
28464     getSelectionCount : function(){
28465         return this.selections.length;
28466     },
28467
28468     /**
28469      * Get the currently selected nodes.
28470      * @return {Array} An array of HTMLElements
28471      */
28472     getSelectedNodes : function(){
28473         return this.selections;
28474     },
28475
28476     /**
28477      * Get the indexes of the selected nodes.
28478      * @return {Array}
28479      */
28480     getSelectedIndexes : function(){
28481         var indexes = [], s = this.selections;
28482         for(var i = 0, len = s.length; i < len; i++){
28483             indexes.push(s[i].nodeIndex);
28484         }
28485         return indexes;
28486     },
28487
28488     /**
28489      * Clear all selections
28490      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28491      */
28492     clearSelections : function(suppressEvent){
28493         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28494             this.cmp.elements = this.selections;
28495             this.cmp.removeClass(this.selectedClass);
28496             this.selections = [];
28497             if(!suppressEvent){
28498                 this.fireEvent("selectionchange", this, this.selections);
28499             }
28500         }
28501     },
28502
28503     /**
28504      * Returns true if the passed node is selected
28505      * @param {HTMLElement/Number} node The node or node index
28506      * @return {Boolean}
28507      */
28508     isSelected : function(node){
28509         var s = this.selections;
28510         if(s.length < 1){
28511             return false;
28512         }
28513         node = this.getNode(node);
28514         return s.indexOf(node) !== -1;
28515     },
28516
28517     /**
28518      * Selects nodes.
28519      * @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
28520      * @param {Boolean} keepExisting (optional) true to keep existing selections
28521      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28522      */
28523     select : function(nodeInfo, keepExisting, suppressEvent){
28524         if(nodeInfo instanceof Array){
28525             if(!keepExisting){
28526                 this.clearSelections(true);
28527             }
28528             for(var i = 0, len = nodeInfo.length; i < len; i++){
28529                 this.select(nodeInfo[i], true, true);
28530             }
28531             return;
28532         } 
28533         var node = this.getNode(nodeInfo);
28534         if(!node || this.isSelected(node)){
28535             return; // already selected.
28536         }
28537         if(!keepExisting){
28538             this.clearSelections(true);
28539         }
28540         
28541         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28542             Roo.fly(node).addClass(this.selectedClass);
28543             this.selections.push(node);
28544             if(!suppressEvent){
28545                 this.fireEvent("selectionchange", this, this.selections);
28546             }
28547         }
28548         
28549         
28550     },
28551       /**
28552      * Unselects nodes.
28553      * @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
28554      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28555      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28556      */
28557     unselect : function(nodeInfo, keepExisting, suppressEvent)
28558     {
28559         if(nodeInfo instanceof Array){
28560             Roo.each(this.selections, function(s) {
28561                 this.unselect(s, nodeInfo);
28562             }, this);
28563             return;
28564         }
28565         var node = this.getNode(nodeInfo);
28566         if(!node || !this.isSelected(node)){
28567             //Roo.log("not selected");
28568             return; // not selected.
28569         }
28570         // fireevent???
28571         var ns = [];
28572         Roo.each(this.selections, function(s) {
28573             if (s == node ) {
28574                 Roo.fly(node).removeClass(this.selectedClass);
28575
28576                 return;
28577             }
28578             ns.push(s);
28579         },this);
28580         
28581         this.selections= ns;
28582         this.fireEvent("selectionchange", this, this.selections);
28583     },
28584
28585     /**
28586      * Gets a template node.
28587      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28588      * @return {HTMLElement} The node or null if it wasn't found
28589      */
28590     getNode : function(nodeInfo){
28591         if(typeof nodeInfo == "string"){
28592             return document.getElementById(nodeInfo);
28593         }else if(typeof nodeInfo == "number"){
28594             return this.nodes[nodeInfo];
28595         }
28596         return nodeInfo;
28597     },
28598
28599     /**
28600      * Gets a range template nodes.
28601      * @param {Number} startIndex
28602      * @param {Number} endIndex
28603      * @return {Array} An array of nodes
28604      */
28605     getNodes : function(start, end){
28606         var ns = this.nodes;
28607         start = start || 0;
28608         end = typeof end == "undefined" ? ns.length - 1 : end;
28609         var nodes = [];
28610         if(start <= end){
28611             for(var i = start; i <= end; i++){
28612                 nodes.push(ns[i]);
28613             }
28614         } else{
28615             for(var i = start; i >= end; i--){
28616                 nodes.push(ns[i]);
28617             }
28618         }
28619         return nodes;
28620     },
28621
28622     /**
28623      * Finds the index of the passed node
28624      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28625      * @return {Number} The index of the node or -1
28626      */
28627     indexOf : function(node){
28628         node = this.getNode(node);
28629         if(typeof node.nodeIndex == "number"){
28630             return node.nodeIndex;
28631         }
28632         var ns = this.nodes;
28633         for(var i = 0, len = ns.length; i < len; i++){
28634             if(ns[i] == node){
28635                 return i;
28636             }
28637         }
28638         return -1;
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  * @class Roo.JsonView
28654  * @extends Roo.View
28655  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28656 <pre><code>
28657 var view = new Roo.JsonView({
28658     container: "my-element",
28659     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28660     multiSelect: true, 
28661     jsonRoot: "data" 
28662 });
28663
28664 // listen for node click?
28665 view.on("click", function(vw, index, node, e){
28666     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28667 });
28668
28669 // direct load of JSON data
28670 view.load("foobar.php");
28671
28672 // Example from my blog list
28673 var tpl = new Roo.Template(
28674     '&lt;div class="entry"&gt;' +
28675     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28676     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28677     "&lt;/div&gt;&lt;hr /&gt;"
28678 );
28679
28680 var moreView = new Roo.JsonView({
28681     container :  "entry-list", 
28682     template : tpl,
28683     jsonRoot: "posts"
28684 });
28685 moreView.on("beforerender", this.sortEntries, this);
28686 moreView.load({
28687     url: "/blog/get-posts.php",
28688     params: "allposts=true",
28689     text: "Loading Blog Entries..."
28690 });
28691 </code></pre>
28692
28693 * Note: old code is supported with arguments : (container, template, config)
28694
28695
28696  * @constructor
28697  * Create a new JsonView
28698  * 
28699  * @param {Object} config The config object
28700  * 
28701  */
28702 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28703     
28704     
28705     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28706
28707     var um = this.el.getUpdateManager();
28708     um.setRenderer(this);
28709     um.on("update", this.onLoad, this);
28710     um.on("failure", this.onLoadException, this);
28711
28712     /**
28713      * @event beforerender
28714      * Fires before rendering of the downloaded JSON data.
28715      * @param {Roo.JsonView} this
28716      * @param {Object} data The JSON data loaded
28717      */
28718     /**
28719      * @event load
28720      * Fires when data is loaded.
28721      * @param {Roo.JsonView} this
28722      * @param {Object} data The JSON data loaded
28723      * @param {Object} response The raw Connect response object
28724      */
28725     /**
28726      * @event loadexception
28727      * Fires when loading fails.
28728      * @param {Roo.JsonView} this
28729      * @param {Object} response The raw Connect response object
28730      */
28731     this.addEvents({
28732         'beforerender' : true,
28733         'load' : true,
28734         'loadexception' : true
28735     });
28736 };
28737 Roo.extend(Roo.JsonView, Roo.View, {
28738     /**
28739      * @type {String} The root property in the loaded JSON object that contains the data
28740      */
28741     jsonRoot : "",
28742
28743     /**
28744      * Refreshes the view.
28745      */
28746     refresh : function(){
28747         this.clearSelections();
28748         this.el.update("");
28749         var html = [];
28750         var o = this.jsonData;
28751         if(o && o.length > 0){
28752             for(var i = 0, len = o.length; i < len; i++){
28753                 var data = this.prepareData(o[i], i, o);
28754                 html[html.length] = this.tpl.apply(data);
28755             }
28756         }else{
28757             html.push(this.emptyText);
28758         }
28759         this.el.update(html.join(""));
28760         this.nodes = this.el.dom.childNodes;
28761         this.updateIndexes(0);
28762     },
28763
28764     /**
28765      * 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.
28766      * @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:
28767      <pre><code>
28768      view.load({
28769          url: "your-url.php",
28770          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28771          callback: yourFunction,
28772          scope: yourObject, //(optional scope)
28773          discardUrl: false,
28774          nocache: false,
28775          text: "Loading...",
28776          timeout: 30,
28777          scripts: false
28778      });
28779      </code></pre>
28780      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28781      * 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.
28782      * @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}
28783      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28784      * @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.
28785      */
28786     load : function(){
28787         var um = this.el.getUpdateManager();
28788         um.update.apply(um, arguments);
28789     },
28790
28791     // note - render is a standard framework call...
28792     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28793     render : function(el, response){
28794         
28795         this.clearSelections();
28796         this.el.update("");
28797         var o;
28798         try{
28799             if (response != '') {
28800                 o = Roo.util.JSON.decode(response.responseText);
28801                 if(this.jsonRoot){
28802                     
28803                     o = o[this.jsonRoot];
28804                 }
28805             }
28806         } catch(e){
28807         }
28808         /**
28809          * The current JSON data or null
28810          */
28811         this.jsonData = o;
28812         this.beforeRender();
28813         this.refresh();
28814     },
28815
28816 /**
28817  * Get the number of records in the current JSON dataset
28818  * @return {Number}
28819  */
28820     getCount : function(){
28821         return this.jsonData ? this.jsonData.length : 0;
28822     },
28823
28824 /**
28825  * Returns the JSON object for the specified node(s)
28826  * @param {HTMLElement/Array} node The node or an array of nodes
28827  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28828  * you get the JSON object for the node
28829  */
28830     getNodeData : function(node){
28831         if(node instanceof Array){
28832             var data = [];
28833             for(var i = 0, len = node.length; i < len; i++){
28834                 data.push(this.getNodeData(node[i]));
28835             }
28836             return data;
28837         }
28838         return this.jsonData[this.indexOf(node)] || null;
28839     },
28840
28841     beforeRender : function(){
28842         this.snapshot = this.jsonData;
28843         if(this.sortInfo){
28844             this.sort.apply(this, this.sortInfo);
28845         }
28846         this.fireEvent("beforerender", this, this.jsonData);
28847     },
28848
28849     onLoad : function(el, o){
28850         this.fireEvent("load", this, this.jsonData, o);
28851     },
28852
28853     onLoadException : function(el, o){
28854         this.fireEvent("loadexception", this, o);
28855     },
28856
28857 /**
28858  * Filter the data by a specific property.
28859  * @param {String} property A property on your JSON objects
28860  * @param {String/RegExp} value Either string that the property values
28861  * should start with, or a RegExp to test against the property
28862  */
28863     filter : function(property, value){
28864         if(this.jsonData){
28865             var data = [];
28866             var ss = this.snapshot;
28867             if(typeof value == "string"){
28868                 var vlen = value.length;
28869                 if(vlen == 0){
28870                     this.clearFilter();
28871                     return;
28872                 }
28873                 value = value.toLowerCase();
28874                 for(var i = 0, len = ss.length; i < len; i++){
28875                     var o = ss[i];
28876                     if(o[property].substr(0, vlen).toLowerCase() == value){
28877                         data.push(o);
28878                     }
28879                 }
28880             } else if(value.exec){ // regex?
28881                 for(var i = 0, len = ss.length; i < len; i++){
28882                     var o = ss[i];
28883                     if(value.test(o[property])){
28884                         data.push(o);
28885                     }
28886                 }
28887             } else{
28888                 return;
28889             }
28890             this.jsonData = data;
28891             this.refresh();
28892         }
28893     },
28894
28895 /**
28896  * Filter by a function. The passed function will be called with each
28897  * object in the current dataset. If the function returns true the value is kept,
28898  * otherwise it is filtered.
28899  * @param {Function} fn
28900  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28901  */
28902     filterBy : function(fn, scope){
28903         if(this.jsonData){
28904             var data = [];
28905             var ss = this.snapshot;
28906             for(var i = 0, len = ss.length; i < len; i++){
28907                 var o = ss[i];
28908                 if(fn.call(scope || this, o)){
28909                     data.push(o);
28910                 }
28911             }
28912             this.jsonData = data;
28913             this.refresh();
28914         }
28915     },
28916
28917 /**
28918  * Clears the current filter.
28919  */
28920     clearFilter : function(){
28921         if(this.snapshot && this.jsonData != this.snapshot){
28922             this.jsonData = this.snapshot;
28923             this.refresh();
28924         }
28925     },
28926
28927
28928 /**
28929  * Sorts the data for this view and refreshes it.
28930  * @param {String} property A property on your JSON objects to sort on
28931  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28932  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28933  */
28934     sort : function(property, dir, sortType){
28935         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28936         if(this.jsonData){
28937             var p = property;
28938             var dsc = dir && dir.toLowerCase() == "desc";
28939             var f = function(o1, o2){
28940                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28941                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28942                 ;
28943                 if(v1 < v2){
28944                     return dsc ? +1 : -1;
28945                 } else if(v1 > v2){
28946                     return dsc ? -1 : +1;
28947                 } else{
28948                     return 0;
28949                 }
28950             };
28951             this.jsonData.sort(f);
28952             this.refresh();
28953             if(this.jsonData != this.snapshot){
28954                 this.snapshot.sort(f);
28955             }
28956         }
28957     }
28958 });/*
28959  * Based on:
28960  * Ext JS Library 1.1.1
28961  * Copyright(c) 2006-2007, Ext JS, LLC.
28962  *
28963  * Originally Released Under LGPL - original licence link has changed is not relivant.
28964  *
28965  * Fork - LGPL
28966  * <script type="text/javascript">
28967  */
28968  
28969
28970 /**
28971  * @class Roo.ColorPalette
28972  * @extends Roo.Component
28973  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28974  * Here's an example of typical usage:
28975  * <pre><code>
28976 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28977 cp.render('my-div');
28978
28979 cp.on('select', function(palette, selColor){
28980     // do something with selColor
28981 });
28982 </code></pre>
28983  * @constructor
28984  * Create a new ColorPalette
28985  * @param {Object} config The config object
28986  */
28987 Roo.ColorPalette = function(config){
28988     Roo.ColorPalette.superclass.constructor.call(this, config);
28989     this.addEvents({
28990         /**
28991              * @event select
28992              * Fires when a color is selected
28993              * @param {ColorPalette} this
28994              * @param {String} color The 6-digit color hex code (without the # symbol)
28995              */
28996         select: true
28997     });
28998
28999     if(this.handler){
29000         this.on("select", this.handler, this.scope, true);
29001     }
29002 };
29003 Roo.extend(Roo.ColorPalette, Roo.Component, {
29004     /**
29005      * @cfg {String} itemCls
29006      * The CSS class to apply to the containing element (defaults to "x-color-palette")
29007      */
29008     itemCls : "x-color-palette",
29009     /**
29010      * @cfg {String} value
29011      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
29012      * the hex codes are case-sensitive.
29013      */
29014     value : null,
29015     clickEvent:'click',
29016     // private
29017     ctype: "Roo.ColorPalette",
29018
29019     /**
29020      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
29021      */
29022     allowReselect : false,
29023
29024     /**
29025      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
29026      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
29027      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
29028      * of colors with the width setting until the box is symmetrical.</p>
29029      * <p>You can override individual colors if needed:</p>
29030      * <pre><code>
29031 var cp = new Roo.ColorPalette();
29032 cp.colors[0] = "FF0000";  // change the first box to red
29033 </code></pre>
29034
29035 Or you can provide a custom array of your own for complete control:
29036 <pre><code>
29037 var cp = new Roo.ColorPalette();
29038 cp.colors = ["000000", "993300", "333300"];
29039 </code></pre>
29040      * @type Array
29041      */
29042     colors : [
29043         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
29044         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
29045         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
29046         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
29047         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
29048     ],
29049
29050     // private
29051     onRender : function(container, position){
29052         var t = new Roo.MasterTemplate(
29053             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
29054         );
29055         var c = this.colors;
29056         for(var i = 0, len = c.length; i < len; i++){
29057             t.add([c[i]]);
29058         }
29059         var el = document.createElement("div");
29060         el.className = this.itemCls;
29061         t.overwrite(el);
29062         container.dom.insertBefore(el, position);
29063         this.el = Roo.get(el);
29064         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
29065         if(this.clickEvent != 'click'){
29066             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
29067         }
29068     },
29069
29070     // private
29071     afterRender : function(){
29072         Roo.ColorPalette.superclass.afterRender.call(this);
29073         if(this.value){
29074             var s = this.value;
29075             this.value = null;
29076             this.select(s);
29077         }
29078     },
29079
29080     // private
29081     handleClick : function(e, t){
29082         e.preventDefault();
29083         if(!this.disabled){
29084             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
29085             this.select(c.toUpperCase());
29086         }
29087     },
29088
29089     /**
29090      * Selects the specified color in the palette (fires the select event)
29091      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
29092      */
29093     select : function(color){
29094         color = color.replace("#", "");
29095         if(color != this.value || this.allowReselect){
29096             var el = this.el;
29097             if(this.value){
29098                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
29099             }
29100             el.child("a.color-"+color).addClass("x-color-palette-sel");
29101             this.value = color;
29102             this.fireEvent("select", this, color);
29103         }
29104     }
29105 });/*
29106  * Based on:
29107  * Ext JS Library 1.1.1
29108  * Copyright(c) 2006-2007, Ext JS, LLC.
29109  *
29110  * Originally Released Under LGPL - original licence link has changed is not relivant.
29111  *
29112  * Fork - LGPL
29113  * <script type="text/javascript">
29114  */
29115  
29116 /**
29117  * @class Roo.DatePicker
29118  * @extends Roo.Component
29119  * Simple date picker class.
29120  * @constructor
29121  * Create a new DatePicker
29122  * @param {Object} config The config object
29123  */
29124 Roo.DatePicker = function(config){
29125     Roo.DatePicker.superclass.constructor.call(this, config);
29126
29127     this.value = config && config.value ?
29128                  config.value.clearTime() : new Date().clearTime();
29129
29130     this.addEvents({
29131         /**
29132              * @event select
29133              * Fires when a date is selected
29134              * @param {DatePicker} this
29135              * @param {Date} date The selected date
29136              */
29137         'select': true,
29138         /**
29139              * @event monthchange
29140              * Fires when the displayed month changes 
29141              * @param {DatePicker} this
29142              * @param {Date} date The selected month
29143              */
29144         'monthchange': true
29145     });
29146
29147     if(this.handler){
29148         this.on("select", this.handler,  this.scope || this);
29149     }
29150     // build the disabledDatesRE
29151     if(!this.disabledDatesRE && this.disabledDates){
29152         var dd = this.disabledDates;
29153         var re = "(?:";
29154         for(var i = 0; i < dd.length; i++){
29155             re += dd[i];
29156             if(i != dd.length-1) {
29157                 re += "|";
29158             }
29159         }
29160         this.disabledDatesRE = new RegExp(re + ")");
29161     }
29162 };
29163
29164 Roo.extend(Roo.DatePicker, Roo.Component, {
29165     /**
29166      * @cfg {String} todayText
29167      * The text to display on the button that selects the current date (defaults to "Today")
29168      */
29169     todayText : "Today",
29170     /**
29171      * @cfg {String} okText
29172      * The text to display on the ok button
29173      */
29174     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
29175     /**
29176      * @cfg {String} cancelText
29177      * The text to display on the cancel button
29178      */
29179     cancelText : "Cancel",
29180     /**
29181      * @cfg {String} todayTip
29182      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
29183      */
29184     todayTip : "{0} (Spacebar)",
29185     /**
29186      * @cfg {Date} minDate
29187      * Minimum allowable date (JavaScript date object, defaults to null)
29188      */
29189     minDate : null,
29190     /**
29191      * @cfg {Date} maxDate
29192      * Maximum allowable date (JavaScript date object, defaults to null)
29193      */
29194     maxDate : null,
29195     /**
29196      * @cfg {String} minText
29197      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
29198      */
29199     minText : "This date is before the minimum date",
29200     /**
29201      * @cfg {String} maxText
29202      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
29203      */
29204     maxText : "This date is after the maximum date",
29205     /**
29206      * @cfg {String} format
29207      * The default date format string which can be overriden for localization support.  The format must be
29208      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
29209      */
29210     format : "m/d/y",
29211     /**
29212      * @cfg {Array} disabledDays
29213      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
29214      */
29215     disabledDays : null,
29216     /**
29217      * @cfg {String} disabledDaysText
29218      * The tooltip to display when the date falls on a disabled day (defaults to "")
29219      */
29220     disabledDaysText : "",
29221     /**
29222      * @cfg {RegExp} disabledDatesRE
29223      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
29224      */
29225     disabledDatesRE : null,
29226     /**
29227      * @cfg {String} disabledDatesText
29228      * The tooltip text to display when the date falls on a disabled date (defaults to "")
29229      */
29230     disabledDatesText : "",
29231     /**
29232      * @cfg {Boolean} constrainToViewport
29233      * True to constrain the date picker to the viewport (defaults to true)
29234      */
29235     constrainToViewport : true,
29236     /**
29237      * @cfg {Array} monthNames
29238      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
29239      */
29240     monthNames : Date.monthNames,
29241     /**
29242      * @cfg {Array} dayNames
29243      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
29244      */
29245     dayNames : Date.dayNames,
29246     /**
29247      * @cfg {String} nextText
29248      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
29249      */
29250     nextText: 'Next Month (Control+Right)',
29251     /**
29252      * @cfg {String} prevText
29253      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
29254      */
29255     prevText: 'Previous Month (Control+Left)',
29256     /**
29257      * @cfg {String} monthYearText
29258      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
29259      */
29260     monthYearText: 'Choose a month (Control+Up/Down to move years)',
29261     /**
29262      * @cfg {Number} startDay
29263      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
29264      */
29265     startDay : 0,
29266     /**
29267      * @cfg {Bool} showClear
29268      * Show a clear button (usefull for date form elements that can be blank.)
29269      */
29270     
29271     showClear: false,
29272     
29273     /**
29274      * Sets the value of the date field
29275      * @param {Date} value The date to set
29276      */
29277     setValue : function(value){
29278         var old = this.value;
29279         
29280         if (typeof(value) == 'string') {
29281          
29282             value = Date.parseDate(value, this.format);
29283         }
29284         if (!value) {
29285             value = new Date();
29286         }
29287         
29288         this.value = value.clearTime(true);
29289         if(this.el){
29290             this.update(this.value);
29291         }
29292     },
29293
29294     /**
29295      * Gets the current selected value of the date field
29296      * @return {Date} The selected date
29297      */
29298     getValue : function(){
29299         return this.value;
29300     },
29301
29302     // private
29303     focus : function(){
29304         if(this.el){
29305             this.update(this.activeDate);
29306         }
29307     },
29308
29309     // privateval
29310     onRender : function(container, position){
29311         
29312         var m = [
29313              '<table cellspacing="0">',
29314                 '<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>',
29315                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29316         var dn = this.dayNames;
29317         for(var i = 0; i < 7; i++){
29318             var d = this.startDay+i;
29319             if(d > 6){
29320                 d = d-7;
29321             }
29322             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29323         }
29324         m[m.length] = "</tr></thead><tbody><tr>";
29325         for(var i = 0; i < 42; i++) {
29326             if(i % 7 == 0 && i != 0){
29327                 m[m.length] = "</tr><tr>";
29328             }
29329             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29330         }
29331         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29332             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29333
29334         var el = document.createElement("div");
29335         el.className = "x-date-picker";
29336         el.innerHTML = m.join("");
29337
29338         container.dom.insertBefore(el, position);
29339
29340         this.el = Roo.get(el);
29341         this.eventEl = Roo.get(el.firstChild);
29342
29343         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29344             handler: this.showPrevMonth,
29345             scope: this,
29346             preventDefault:true,
29347             stopDefault:true
29348         });
29349
29350         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29351             handler: this.showNextMonth,
29352             scope: this,
29353             preventDefault:true,
29354             stopDefault:true
29355         });
29356
29357         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29358
29359         this.monthPicker = this.el.down('div.x-date-mp');
29360         this.monthPicker.enableDisplayMode('block');
29361         
29362         var kn = new Roo.KeyNav(this.eventEl, {
29363             "left" : function(e){
29364                 e.ctrlKey ?
29365                     this.showPrevMonth() :
29366                     this.update(this.activeDate.add("d", -1));
29367             },
29368
29369             "right" : function(e){
29370                 e.ctrlKey ?
29371                     this.showNextMonth() :
29372                     this.update(this.activeDate.add("d", 1));
29373             },
29374
29375             "up" : function(e){
29376                 e.ctrlKey ?
29377                     this.showNextYear() :
29378                     this.update(this.activeDate.add("d", -7));
29379             },
29380
29381             "down" : function(e){
29382                 e.ctrlKey ?
29383                     this.showPrevYear() :
29384                     this.update(this.activeDate.add("d", 7));
29385             },
29386
29387             "pageUp" : function(e){
29388                 this.showNextMonth();
29389             },
29390
29391             "pageDown" : function(e){
29392                 this.showPrevMonth();
29393             },
29394
29395             "enter" : function(e){
29396                 e.stopPropagation();
29397                 return true;
29398             },
29399
29400             scope : this
29401         });
29402
29403         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29404
29405         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29406
29407         this.el.unselectable();
29408         
29409         this.cells = this.el.select("table.x-date-inner tbody td");
29410         this.textNodes = this.el.query("table.x-date-inner tbody span");
29411
29412         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29413             text: "&#160;",
29414             tooltip: this.monthYearText
29415         });
29416
29417         this.mbtn.on('click', this.showMonthPicker, this);
29418         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29419
29420
29421         var today = (new Date()).dateFormat(this.format);
29422         
29423         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29424         if (this.showClear) {
29425             baseTb.add( new Roo.Toolbar.Fill());
29426         }
29427         baseTb.add({
29428             text: String.format(this.todayText, today),
29429             tooltip: String.format(this.todayTip, today),
29430             handler: this.selectToday,
29431             scope: this
29432         });
29433         
29434         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29435             
29436         //});
29437         if (this.showClear) {
29438             
29439             baseTb.add( new Roo.Toolbar.Fill());
29440             baseTb.add({
29441                 text: '&#160;',
29442                 cls: 'x-btn-icon x-btn-clear',
29443                 handler: function() {
29444                     //this.value = '';
29445                     this.fireEvent("select", this, '');
29446                 },
29447                 scope: this
29448             });
29449         }
29450         
29451         
29452         if(Roo.isIE){
29453             this.el.repaint();
29454         }
29455         this.update(this.value);
29456     },
29457
29458     createMonthPicker : function(){
29459         if(!this.monthPicker.dom.firstChild){
29460             var buf = ['<table border="0" cellspacing="0">'];
29461             for(var i = 0; i < 6; i++){
29462                 buf.push(
29463                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29464                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29465                     i == 0 ?
29466                     '<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>' :
29467                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29468                 );
29469             }
29470             buf.push(
29471                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29472                     this.okText,
29473                     '</button><button type="button" class="x-date-mp-cancel">',
29474                     this.cancelText,
29475                     '</button></td></tr>',
29476                 '</table>'
29477             );
29478             this.monthPicker.update(buf.join(''));
29479             this.monthPicker.on('click', this.onMonthClick, this);
29480             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29481
29482             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29483             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29484
29485             this.mpMonths.each(function(m, a, i){
29486                 i += 1;
29487                 if((i%2) == 0){
29488                     m.dom.xmonth = 5 + Math.round(i * .5);
29489                 }else{
29490                     m.dom.xmonth = Math.round((i-1) * .5);
29491                 }
29492             });
29493         }
29494     },
29495
29496     showMonthPicker : function(){
29497         this.createMonthPicker();
29498         var size = this.el.getSize();
29499         this.monthPicker.setSize(size);
29500         this.monthPicker.child('table').setSize(size);
29501
29502         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29503         this.updateMPMonth(this.mpSelMonth);
29504         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29505         this.updateMPYear(this.mpSelYear);
29506
29507         this.monthPicker.slideIn('t', {duration:.2});
29508     },
29509
29510     updateMPYear : function(y){
29511         this.mpyear = y;
29512         var ys = this.mpYears.elements;
29513         for(var i = 1; i <= 10; i++){
29514             var td = ys[i-1], y2;
29515             if((i%2) == 0){
29516                 y2 = y + Math.round(i * .5);
29517                 td.firstChild.innerHTML = y2;
29518                 td.xyear = y2;
29519             }else{
29520                 y2 = y - (5-Math.round(i * .5));
29521                 td.firstChild.innerHTML = y2;
29522                 td.xyear = y2;
29523             }
29524             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29525         }
29526     },
29527
29528     updateMPMonth : function(sm){
29529         this.mpMonths.each(function(m, a, i){
29530             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29531         });
29532     },
29533
29534     selectMPMonth: function(m){
29535         
29536     },
29537
29538     onMonthClick : function(e, t){
29539         e.stopEvent();
29540         var el = new Roo.Element(t), pn;
29541         if(el.is('button.x-date-mp-cancel')){
29542             this.hideMonthPicker();
29543         }
29544         else if(el.is('button.x-date-mp-ok')){
29545             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29546             this.hideMonthPicker();
29547         }
29548         else if(pn = el.up('td.x-date-mp-month', 2)){
29549             this.mpMonths.removeClass('x-date-mp-sel');
29550             pn.addClass('x-date-mp-sel');
29551             this.mpSelMonth = pn.dom.xmonth;
29552         }
29553         else if(pn = el.up('td.x-date-mp-year', 2)){
29554             this.mpYears.removeClass('x-date-mp-sel');
29555             pn.addClass('x-date-mp-sel');
29556             this.mpSelYear = pn.dom.xyear;
29557         }
29558         else if(el.is('a.x-date-mp-prev')){
29559             this.updateMPYear(this.mpyear-10);
29560         }
29561         else if(el.is('a.x-date-mp-next')){
29562             this.updateMPYear(this.mpyear+10);
29563         }
29564     },
29565
29566     onMonthDblClick : function(e, t){
29567         e.stopEvent();
29568         var el = new Roo.Element(t), pn;
29569         if(pn = el.up('td.x-date-mp-month', 2)){
29570             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29571             this.hideMonthPicker();
29572         }
29573         else if(pn = el.up('td.x-date-mp-year', 2)){
29574             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29575             this.hideMonthPicker();
29576         }
29577     },
29578
29579     hideMonthPicker : function(disableAnim){
29580         if(this.monthPicker){
29581             if(disableAnim === true){
29582                 this.monthPicker.hide();
29583             }else{
29584                 this.monthPicker.slideOut('t', {duration:.2});
29585             }
29586         }
29587     },
29588
29589     // private
29590     showPrevMonth : function(e){
29591         this.update(this.activeDate.add("mo", -1));
29592     },
29593
29594     // private
29595     showNextMonth : function(e){
29596         this.update(this.activeDate.add("mo", 1));
29597     },
29598
29599     // private
29600     showPrevYear : function(){
29601         this.update(this.activeDate.add("y", -1));
29602     },
29603
29604     // private
29605     showNextYear : function(){
29606         this.update(this.activeDate.add("y", 1));
29607     },
29608
29609     // private
29610     handleMouseWheel : function(e){
29611         var delta = e.getWheelDelta();
29612         if(delta > 0){
29613             this.showPrevMonth();
29614             e.stopEvent();
29615         } else if(delta < 0){
29616             this.showNextMonth();
29617             e.stopEvent();
29618         }
29619     },
29620
29621     // private
29622     handleDateClick : function(e, t){
29623         e.stopEvent();
29624         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29625             this.setValue(new Date(t.dateValue));
29626             this.fireEvent("select", this, this.value);
29627         }
29628     },
29629
29630     // private
29631     selectToday : function(){
29632         this.setValue(new Date().clearTime());
29633         this.fireEvent("select", this, this.value);
29634     },
29635
29636     // private
29637     update : function(date)
29638     {
29639         var vd = this.activeDate;
29640         this.activeDate = date;
29641         if(vd && this.el){
29642             var t = date.getTime();
29643             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29644                 this.cells.removeClass("x-date-selected");
29645                 this.cells.each(function(c){
29646                    if(c.dom.firstChild.dateValue == t){
29647                        c.addClass("x-date-selected");
29648                        setTimeout(function(){
29649                             try{c.dom.firstChild.focus();}catch(e){}
29650                        }, 50);
29651                        return false;
29652                    }
29653                 });
29654                 return;
29655             }
29656         }
29657         
29658         var days = date.getDaysInMonth();
29659         var firstOfMonth = date.getFirstDateOfMonth();
29660         var startingPos = firstOfMonth.getDay()-this.startDay;
29661
29662         if(startingPos <= this.startDay){
29663             startingPos += 7;
29664         }
29665
29666         var pm = date.add("mo", -1);
29667         var prevStart = pm.getDaysInMonth()-startingPos;
29668
29669         var cells = this.cells.elements;
29670         var textEls = this.textNodes;
29671         days += startingPos;
29672
29673         // convert everything to numbers so it's fast
29674         var day = 86400000;
29675         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29676         var today = new Date().clearTime().getTime();
29677         var sel = date.clearTime().getTime();
29678         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29679         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29680         var ddMatch = this.disabledDatesRE;
29681         var ddText = this.disabledDatesText;
29682         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29683         var ddaysText = this.disabledDaysText;
29684         var format = this.format;
29685
29686         var setCellClass = function(cal, cell){
29687             cell.title = "";
29688             var t = d.getTime();
29689             cell.firstChild.dateValue = t;
29690             if(t == today){
29691                 cell.className += " x-date-today";
29692                 cell.title = cal.todayText;
29693             }
29694             if(t == sel){
29695                 cell.className += " x-date-selected";
29696                 setTimeout(function(){
29697                     try{cell.firstChild.focus();}catch(e){}
29698                 }, 50);
29699             }
29700             // disabling
29701             if(t < min) {
29702                 cell.className = " x-date-disabled";
29703                 cell.title = cal.minText;
29704                 return;
29705             }
29706             if(t > max) {
29707                 cell.className = " x-date-disabled";
29708                 cell.title = cal.maxText;
29709                 return;
29710             }
29711             if(ddays){
29712                 if(ddays.indexOf(d.getDay()) != -1){
29713                     cell.title = ddaysText;
29714                     cell.className = " x-date-disabled";
29715                 }
29716             }
29717             if(ddMatch && format){
29718                 var fvalue = d.dateFormat(format);
29719                 if(ddMatch.test(fvalue)){
29720                     cell.title = ddText.replace("%0", fvalue);
29721                     cell.className = " x-date-disabled";
29722                 }
29723             }
29724         };
29725
29726         var i = 0;
29727         for(; i < startingPos; i++) {
29728             textEls[i].innerHTML = (++prevStart);
29729             d.setDate(d.getDate()+1);
29730             cells[i].className = "x-date-prevday";
29731             setCellClass(this, cells[i]);
29732         }
29733         for(; i < days; i++){
29734             intDay = i - startingPos + 1;
29735             textEls[i].innerHTML = (intDay);
29736             d.setDate(d.getDate()+1);
29737             cells[i].className = "x-date-active";
29738             setCellClass(this, cells[i]);
29739         }
29740         var extraDays = 0;
29741         for(; i < 42; i++) {
29742              textEls[i].innerHTML = (++extraDays);
29743              d.setDate(d.getDate()+1);
29744              cells[i].className = "x-date-nextday";
29745              setCellClass(this, cells[i]);
29746         }
29747
29748         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29749         this.fireEvent('monthchange', this, date);
29750         
29751         if(!this.internalRender){
29752             var main = this.el.dom.firstChild;
29753             var w = main.offsetWidth;
29754             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29755             Roo.fly(main).setWidth(w);
29756             this.internalRender = true;
29757             // opera does not respect the auto grow header center column
29758             // then, after it gets a width opera refuses to recalculate
29759             // without a second pass
29760             if(Roo.isOpera && !this.secondPass){
29761                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29762                 this.secondPass = true;
29763                 this.update.defer(10, this, [date]);
29764             }
29765         }
29766         
29767         
29768     }
29769 });        /*
29770  * Based on:
29771  * Ext JS Library 1.1.1
29772  * Copyright(c) 2006-2007, Ext JS, LLC.
29773  *
29774  * Originally Released Under LGPL - original licence link has changed is not relivant.
29775  *
29776  * Fork - LGPL
29777  * <script type="text/javascript">
29778  */
29779 /**
29780  * @class Roo.TabPanel
29781  * @extends Roo.util.Observable
29782  * A lightweight tab container.
29783  * <br><br>
29784  * Usage:
29785  * <pre><code>
29786 // basic tabs 1, built from existing content
29787 var tabs = new Roo.TabPanel("tabs1");
29788 tabs.addTab("script", "View Script");
29789 tabs.addTab("markup", "View Markup");
29790 tabs.activate("script");
29791
29792 // more advanced tabs, built from javascript
29793 var jtabs = new Roo.TabPanel("jtabs");
29794 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29795
29796 // set up the UpdateManager
29797 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29798 var updater = tab2.getUpdateManager();
29799 updater.setDefaultUrl("ajax1.htm");
29800 tab2.on('activate', updater.refresh, updater, true);
29801
29802 // Use setUrl for Ajax loading
29803 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29804 tab3.setUrl("ajax2.htm", null, true);
29805
29806 // Disabled tab
29807 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29808 tab4.disable();
29809
29810 jtabs.activate("jtabs-1");
29811  * </code></pre>
29812  * @constructor
29813  * Create a new TabPanel.
29814  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29815  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29816  */
29817 Roo.TabPanel = function(container, config){
29818     /**
29819     * The container element for this TabPanel.
29820     * @type Roo.Element
29821     */
29822     this.el = Roo.get(container, true);
29823     if(config){
29824         if(typeof config == "boolean"){
29825             this.tabPosition = config ? "bottom" : "top";
29826         }else{
29827             Roo.apply(this, config);
29828         }
29829     }
29830     if(this.tabPosition == "bottom"){
29831         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29832         this.el.addClass("x-tabs-bottom");
29833     }
29834     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29835     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29836     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29837     if(Roo.isIE){
29838         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29839     }
29840     if(this.tabPosition != "bottom"){
29841         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29842          * @type Roo.Element
29843          */
29844         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29845         this.el.addClass("x-tabs-top");
29846     }
29847     this.items = [];
29848
29849     this.bodyEl.setStyle("position", "relative");
29850
29851     this.active = null;
29852     this.activateDelegate = this.activate.createDelegate(this);
29853
29854     this.addEvents({
29855         /**
29856          * @event tabchange
29857          * Fires when the active tab changes
29858          * @param {Roo.TabPanel} this
29859          * @param {Roo.TabPanelItem} activePanel The new active tab
29860          */
29861         "tabchange": true,
29862         /**
29863          * @event beforetabchange
29864          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29865          * @param {Roo.TabPanel} this
29866          * @param {Object} e Set cancel to true on this object to cancel the tab change
29867          * @param {Roo.TabPanelItem} tab The tab being changed to
29868          */
29869         "beforetabchange" : true
29870     });
29871
29872     Roo.EventManager.onWindowResize(this.onResize, this);
29873     this.cpad = this.el.getPadding("lr");
29874     this.hiddenCount = 0;
29875
29876
29877     // toolbar on the tabbar support...
29878     if (this.toolbar) {
29879         var tcfg = this.toolbar;
29880         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29881         this.toolbar = new Roo.Toolbar(tcfg);
29882         if (Roo.isSafari) {
29883             var tbl = tcfg.container.child('table', true);
29884             tbl.setAttribute('width', '100%');
29885         }
29886         
29887     }
29888    
29889
29890
29891     Roo.TabPanel.superclass.constructor.call(this);
29892 };
29893
29894 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29895     /*
29896      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29897      */
29898     tabPosition : "top",
29899     /*
29900      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29901      */
29902     currentTabWidth : 0,
29903     /*
29904      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29905      */
29906     minTabWidth : 40,
29907     /*
29908      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29909      */
29910     maxTabWidth : 250,
29911     /*
29912      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29913      */
29914     preferredTabWidth : 175,
29915     /*
29916      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29917      */
29918     resizeTabs : false,
29919     /*
29920      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29921      */
29922     monitorResize : true,
29923     /*
29924      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29925      */
29926     toolbar : false,
29927
29928     /**
29929      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29930      * @param {String} id The id of the div to use <b>or create</b>
29931      * @param {String} text The text for the tab
29932      * @param {String} content (optional) Content to put in the TabPanelItem body
29933      * @param {Boolean} closable (optional) True to create a close icon on the tab
29934      * @return {Roo.TabPanelItem} The created TabPanelItem
29935      */
29936     addTab : function(id, text, content, closable){
29937         var item = new Roo.TabPanelItem(this, id, text, closable);
29938         this.addTabItem(item);
29939         if(content){
29940             item.setContent(content);
29941         }
29942         return item;
29943     },
29944
29945     /**
29946      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29947      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29948      * @return {Roo.TabPanelItem}
29949      */
29950     getTab : function(id){
29951         return this.items[id];
29952     },
29953
29954     /**
29955      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29956      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29957      */
29958     hideTab : function(id){
29959         var t = this.items[id];
29960         if(!t.isHidden()){
29961            t.setHidden(true);
29962            this.hiddenCount++;
29963            this.autoSizeTabs();
29964         }
29965     },
29966
29967     /**
29968      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29969      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29970      */
29971     unhideTab : function(id){
29972         var t = this.items[id];
29973         if(t.isHidden()){
29974            t.setHidden(false);
29975            this.hiddenCount--;
29976            this.autoSizeTabs();
29977         }
29978     },
29979
29980     /**
29981      * Adds an existing {@link Roo.TabPanelItem}.
29982      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29983      */
29984     addTabItem : function(item){
29985         this.items[item.id] = item;
29986         this.items.push(item);
29987         if(this.resizeTabs){
29988            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29989            this.autoSizeTabs();
29990         }else{
29991             item.autoSize();
29992         }
29993     },
29994
29995     /**
29996      * Removes a {@link Roo.TabPanelItem}.
29997      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29998      */
29999     removeTab : function(id){
30000         var items = this.items;
30001         var tab = items[id];
30002         if(!tab) { return; }
30003         var index = items.indexOf(tab);
30004         if(this.active == tab && items.length > 1){
30005             var newTab = this.getNextAvailable(index);
30006             if(newTab) {
30007                 newTab.activate();
30008             }
30009         }
30010         this.stripEl.dom.removeChild(tab.pnode.dom);
30011         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
30012             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
30013         }
30014         items.splice(index, 1);
30015         delete this.items[tab.id];
30016         tab.fireEvent("close", tab);
30017         tab.purgeListeners();
30018         this.autoSizeTabs();
30019     },
30020
30021     getNextAvailable : function(start){
30022         var items = this.items;
30023         var index = start;
30024         // look for a next tab that will slide over to
30025         // replace the one being removed
30026         while(index < items.length){
30027             var item = items[++index];
30028             if(item && !item.isHidden()){
30029                 return item;
30030             }
30031         }
30032         // if one isn't found select the previous tab (on the left)
30033         index = start;
30034         while(index >= 0){
30035             var item = items[--index];
30036             if(item && !item.isHidden()){
30037                 return item;
30038             }
30039         }
30040         return null;
30041     },
30042
30043     /**
30044      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
30045      * @param {String/Number} id The id or index of the TabPanelItem to disable.
30046      */
30047     disableTab : function(id){
30048         var tab = this.items[id];
30049         if(tab && this.active != tab){
30050             tab.disable();
30051         }
30052     },
30053
30054     /**
30055      * Enables a {@link Roo.TabPanelItem} that is disabled.
30056      * @param {String/Number} id The id or index of the TabPanelItem to enable.
30057      */
30058     enableTab : function(id){
30059         var tab = this.items[id];
30060         tab.enable();
30061     },
30062
30063     /**
30064      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
30065      * @param {String/Number} id The id or index of the TabPanelItem to activate.
30066      * @return {Roo.TabPanelItem} The TabPanelItem.
30067      */
30068     activate : function(id){
30069         var tab = this.items[id];
30070         if(!tab){
30071             return null;
30072         }
30073         if(tab == this.active || tab.disabled){
30074             return tab;
30075         }
30076         var e = {};
30077         this.fireEvent("beforetabchange", this, e, tab);
30078         if(e.cancel !== true && !tab.disabled){
30079             if(this.active){
30080                 this.active.hide();
30081             }
30082             this.active = this.items[id];
30083             this.active.show();
30084             this.fireEvent("tabchange", this, this.active);
30085         }
30086         return tab;
30087     },
30088
30089     /**
30090      * Gets the active {@link Roo.TabPanelItem}.
30091      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
30092      */
30093     getActiveTab : function(){
30094         return this.active;
30095     },
30096
30097     /**
30098      * Updates the tab body element to fit the height of the container element
30099      * for overflow scrolling
30100      * @param {Number} targetHeight (optional) Override the starting height from the elements height
30101      */
30102     syncHeight : function(targetHeight){
30103         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30104         var bm = this.bodyEl.getMargins();
30105         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
30106         this.bodyEl.setHeight(newHeight);
30107         return newHeight;
30108     },
30109
30110     onResize : function(){
30111         if(this.monitorResize){
30112             this.autoSizeTabs();
30113         }
30114     },
30115
30116     /**
30117      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
30118      */
30119     beginUpdate : function(){
30120         this.updating = true;
30121     },
30122
30123     /**
30124      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
30125      */
30126     endUpdate : function(){
30127         this.updating = false;
30128         this.autoSizeTabs();
30129     },
30130
30131     /**
30132      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
30133      */
30134     autoSizeTabs : function(){
30135         var count = this.items.length;
30136         var vcount = count - this.hiddenCount;
30137         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
30138             return;
30139         }
30140         var w = Math.max(this.el.getWidth() - this.cpad, 10);
30141         var availWidth = Math.floor(w / vcount);
30142         var b = this.stripBody;
30143         if(b.getWidth() > w){
30144             var tabs = this.items;
30145             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
30146             if(availWidth < this.minTabWidth){
30147                 /*if(!this.sleft){    // incomplete scrolling code
30148                     this.createScrollButtons();
30149                 }
30150                 this.showScroll();
30151                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
30152             }
30153         }else{
30154             if(this.currentTabWidth < this.preferredTabWidth){
30155                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
30156             }
30157         }
30158     },
30159
30160     /**
30161      * Returns the number of tabs in this TabPanel.
30162      * @return {Number}
30163      */
30164      getCount : function(){
30165          return this.items.length;
30166      },
30167
30168     /**
30169      * Resizes all the tabs to the passed width
30170      * @param {Number} The new width
30171      */
30172     setTabWidth : function(width){
30173         this.currentTabWidth = width;
30174         for(var i = 0, len = this.items.length; i < len; i++) {
30175                 if(!this.items[i].isHidden()) {
30176                 this.items[i].setWidth(width);
30177             }
30178         }
30179     },
30180
30181     /**
30182      * Destroys this TabPanel
30183      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
30184      */
30185     destroy : function(removeEl){
30186         Roo.EventManager.removeResizeListener(this.onResize, this);
30187         for(var i = 0, len = this.items.length; i < len; i++){
30188             this.items[i].purgeListeners();
30189         }
30190         if(removeEl === true){
30191             this.el.update("");
30192             this.el.remove();
30193         }
30194     }
30195 });
30196
30197 /**
30198  * @class Roo.TabPanelItem
30199  * @extends Roo.util.Observable
30200  * Represents an individual item (tab plus body) in a TabPanel.
30201  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
30202  * @param {String} id The id of this TabPanelItem
30203  * @param {String} text The text for the tab of this TabPanelItem
30204  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
30205  */
30206 Roo.TabPanelItem = function(tabPanel, id, text, closable){
30207     /**
30208      * The {@link Roo.TabPanel} this TabPanelItem belongs to
30209      * @type Roo.TabPanel
30210      */
30211     this.tabPanel = tabPanel;
30212     /**
30213      * The id for this TabPanelItem
30214      * @type String
30215      */
30216     this.id = id;
30217     /** @private */
30218     this.disabled = false;
30219     /** @private */
30220     this.text = text;
30221     /** @private */
30222     this.loaded = false;
30223     this.closable = closable;
30224
30225     /**
30226      * The body element for this TabPanelItem.
30227      * @type Roo.Element
30228      */
30229     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
30230     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
30231     this.bodyEl.setStyle("display", "block");
30232     this.bodyEl.setStyle("zoom", "1");
30233     this.hideAction();
30234
30235     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
30236     /** @private */
30237     this.el = Roo.get(els.el, true);
30238     this.inner = Roo.get(els.inner, true);
30239     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
30240     this.pnode = Roo.get(els.el.parentNode, true);
30241     this.el.on("mousedown", this.onTabMouseDown, this);
30242     this.el.on("click", this.onTabClick, this);
30243     /** @private */
30244     if(closable){
30245         var c = Roo.get(els.close, true);
30246         c.dom.title = this.closeText;
30247         c.addClassOnOver("close-over");
30248         c.on("click", this.closeClick, this);
30249      }
30250
30251     this.addEvents({
30252          /**
30253          * @event activate
30254          * Fires when this tab becomes the active tab.
30255          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30256          * @param {Roo.TabPanelItem} this
30257          */
30258         "activate": true,
30259         /**
30260          * @event beforeclose
30261          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
30262          * @param {Roo.TabPanelItem} this
30263          * @param {Object} e Set cancel to true on this object to cancel the close.
30264          */
30265         "beforeclose": true,
30266         /**
30267          * @event close
30268          * Fires when this tab is closed.
30269          * @param {Roo.TabPanelItem} this
30270          */
30271          "close": true,
30272         /**
30273          * @event deactivate
30274          * Fires when this tab is no longer the active tab.
30275          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30276          * @param {Roo.TabPanelItem} this
30277          */
30278          "deactivate" : true
30279     });
30280     this.hidden = false;
30281
30282     Roo.TabPanelItem.superclass.constructor.call(this);
30283 };
30284
30285 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
30286     purgeListeners : function(){
30287        Roo.util.Observable.prototype.purgeListeners.call(this);
30288        this.el.removeAllListeners();
30289     },
30290     /**
30291      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
30292      */
30293     show : function(){
30294         this.pnode.addClass("on");
30295         this.showAction();
30296         if(Roo.isOpera){
30297             this.tabPanel.stripWrap.repaint();
30298         }
30299         this.fireEvent("activate", this.tabPanel, this);
30300     },
30301
30302     /**
30303      * Returns true if this tab is the active tab.
30304      * @return {Boolean}
30305      */
30306     isActive : function(){
30307         return this.tabPanel.getActiveTab() == this;
30308     },
30309
30310     /**
30311      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
30312      */
30313     hide : function(){
30314         this.pnode.removeClass("on");
30315         this.hideAction();
30316         this.fireEvent("deactivate", this.tabPanel, this);
30317     },
30318
30319     hideAction : function(){
30320         this.bodyEl.hide();
30321         this.bodyEl.setStyle("position", "absolute");
30322         this.bodyEl.setLeft("-20000px");
30323         this.bodyEl.setTop("-20000px");
30324     },
30325
30326     showAction : function(){
30327         this.bodyEl.setStyle("position", "relative");
30328         this.bodyEl.setTop("");
30329         this.bodyEl.setLeft("");
30330         this.bodyEl.show();
30331     },
30332
30333     /**
30334      * Set the tooltip for the tab.
30335      * @param {String} tooltip The tab's tooltip
30336      */
30337     setTooltip : function(text){
30338         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30339             this.textEl.dom.qtip = text;
30340             this.textEl.dom.removeAttribute('title');
30341         }else{
30342             this.textEl.dom.title = text;
30343         }
30344     },
30345
30346     onTabClick : function(e){
30347         e.preventDefault();
30348         this.tabPanel.activate(this.id);
30349     },
30350
30351     onTabMouseDown : function(e){
30352         e.preventDefault();
30353         this.tabPanel.activate(this.id);
30354     },
30355
30356     getWidth : function(){
30357         return this.inner.getWidth();
30358     },
30359
30360     setWidth : function(width){
30361         var iwidth = width - this.pnode.getPadding("lr");
30362         this.inner.setWidth(iwidth);
30363         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30364         this.pnode.setWidth(width);
30365     },
30366
30367     /**
30368      * Show or hide the tab
30369      * @param {Boolean} hidden True to hide or false to show.
30370      */
30371     setHidden : function(hidden){
30372         this.hidden = hidden;
30373         this.pnode.setStyle("display", hidden ? "none" : "");
30374     },
30375
30376     /**
30377      * Returns true if this tab is "hidden"
30378      * @return {Boolean}
30379      */
30380     isHidden : function(){
30381         return this.hidden;
30382     },
30383
30384     /**
30385      * Returns the text for this tab
30386      * @return {String}
30387      */
30388     getText : function(){
30389         return this.text;
30390     },
30391
30392     autoSize : function(){
30393         //this.el.beginMeasure();
30394         this.textEl.setWidth(1);
30395         /*
30396          *  #2804 [new] Tabs in Roojs
30397          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30398          */
30399         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30400         //this.el.endMeasure();
30401     },
30402
30403     /**
30404      * Sets the text for the tab (Note: this also sets the tooltip text)
30405      * @param {String} text The tab's text and tooltip
30406      */
30407     setText : function(text){
30408         this.text = text;
30409         this.textEl.update(text);
30410         this.setTooltip(text);
30411         if(!this.tabPanel.resizeTabs){
30412             this.autoSize();
30413         }
30414     },
30415     /**
30416      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30417      */
30418     activate : function(){
30419         this.tabPanel.activate(this.id);
30420     },
30421
30422     /**
30423      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30424      */
30425     disable : function(){
30426         if(this.tabPanel.active != this){
30427             this.disabled = true;
30428             this.pnode.addClass("disabled");
30429         }
30430     },
30431
30432     /**
30433      * Enables this TabPanelItem if it was previously disabled.
30434      */
30435     enable : function(){
30436         this.disabled = false;
30437         this.pnode.removeClass("disabled");
30438     },
30439
30440     /**
30441      * Sets the content for this TabPanelItem.
30442      * @param {String} content The content
30443      * @param {Boolean} loadScripts true to look for and load scripts
30444      */
30445     setContent : function(content, loadScripts){
30446         this.bodyEl.update(content, loadScripts);
30447     },
30448
30449     /**
30450      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30451      * @return {Roo.UpdateManager} The UpdateManager
30452      */
30453     getUpdateManager : function(){
30454         return this.bodyEl.getUpdateManager();
30455     },
30456
30457     /**
30458      * Set a URL to be used to load the content for this TabPanelItem.
30459      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30460      * @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)
30461      * @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)
30462      * @return {Roo.UpdateManager} The UpdateManager
30463      */
30464     setUrl : function(url, params, loadOnce){
30465         if(this.refreshDelegate){
30466             this.un('activate', this.refreshDelegate);
30467         }
30468         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30469         this.on("activate", this.refreshDelegate);
30470         return this.bodyEl.getUpdateManager();
30471     },
30472
30473     /** @private */
30474     _handleRefresh : function(url, params, loadOnce){
30475         if(!loadOnce || !this.loaded){
30476             var updater = this.bodyEl.getUpdateManager();
30477             updater.update(url, params, this._setLoaded.createDelegate(this));
30478         }
30479     },
30480
30481     /**
30482      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30483      *   Will fail silently if the setUrl method has not been called.
30484      *   This does not activate the panel, just updates its content.
30485      */
30486     refresh : function(){
30487         if(this.refreshDelegate){
30488            this.loaded = false;
30489            this.refreshDelegate();
30490         }
30491     },
30492
30493     /** @private */
30494     _setLoaded : function(){
30495         this.loaded = true;
30496     },
30497
30498     /** @private */
30499     closeClick : function(e){
30500         var o = {};
30501         e.stopEvent();
30502         this.fireEvent("beforeclose", this, o);
30503         if(o.cancel !== true){
30504             this.tabPanel.removeTab(this.id);
30505         }
30506     },
30507     /**
30508      * The text displayed in the tooltip for the close icon.
30509      * @type String
30510      */
30511     closeText : "Close this tab"
30512 });
30513
30514 /** @private */
30515 Roo.TabPanel.prototype.createStrip = function(container){
30516     var strip = document.createElement("div");
30517     strip.className = "x-tabs-wrap";
30518     container.appendChild(strip);
30519     return strip;
30520 };
30521 /** @private */
30522 Roo.TabPanel.prototype.createStripList = function(strip){
30523     // div wrapper for retard IE
30524     // returns the "tr" element.
30525     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30526         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30527         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30528     return strip.firstChild.firstChild.firstChild.firstChild;
30529 };
30530 /** @private */
30531 Roo.TabPanel.prototype.createBody = function(container){
30532     var body = document.createElement("div");
30533     Roo.id(body, "tab-body");
30534     Roo.fly(body).addClass("x-tabs-body");
30535     container.appendChild(body);
30536     return body;
30537 };
30538 /** @private */
30539 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30540     var body = Roo.getDom(id);
30541     if(!body){
30542         body = document.createElement("div");
30543         body.id = id;
30544     }
30545     Roo.fly(body).addClass("x-tabs-item-body");
30546     bodyEl.insertBefore(body, bodyEl.firstChild);
30547     return body;
30548 };
30549 /** @private */
30550 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30551     var td = document.createElement("td");
30552     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30553     //stripEl.appendChild(td);
30554     if(closable){
30555         td.className = "x-tabs-closable";
30556         if(!this.closeTpl){
30557             this.closeTpl = new Roo.Template(
30558                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30559                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30560                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30561             );
30562         }
30563         var el = this.closeTpl.overwrite(td, {"text": text});
30564         var close = el.getElementsByTagName("div")[0];
30565         var inner = el.getElementsByTagName("em")[0];
30566         return {"el": el, "close": close, "inner": inner};
30567     } else {
30568         if(!this.tabTpl){
30569             this.tabTpl = new Roo.Template(
30570                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30571                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30572             );
30573         }
30574         var el = this.tabTpl.overwrite(td, {"text": text});
30575         var inner = el.getElementsByTagName("em")[0];
30576         return {"el": el, "inner": inner};
30577     }
30578 };/*
30579  * Based on:
30580  * Ext JS Library 1.1.1
30581  * Copyright(c) 2006-2007, Ext JS, LLC.
30582  *
30583  * Originally Released Under LGPL - original licence link has changed is not relivant.
30584  *
30585  * Fork - LGPL
30586  * <script type="text/javascript">
30587  */
30588
30589 /**
30590  * @class Roo.Button
30591  * @extends Roo.util.Observable
30592  * Simple Button class
30593  * @cfg {String} text The button text
30594  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30595  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30596  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30597  * @cfg {Object} scope The scope of the handler
30598  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30599  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30600  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30601  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30602  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30603  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30604    applies if enableToggle = true)
30605  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30606  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30607   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30608  * @constructor
30609  * Create a new button
30610  * @param {Object} config The config object
30611  */
30612 Roo.Button = function(renderTo, config)
30613 {
30614     if (!config) {
30615         config = renderTo;
30616         renderTo = config.renderTo || false;
30617     }
30618     
30619     Roo.apply(this, config);
30620     this.addEvents({
30621         /**
30622              * @event click
30623              * Fires when this button is clicked
30624              * @param {Button} this
30625              * @param {EventObject} e The click event
30626              */
30627             "click" : true,
30628         /**
30629              * @event toggle
30630              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30631              * @param {Button} this
30632              * @param {Boolean} pressed
30633              */
30634             "toggle" : true,
30635         /**
30636              * @event mouseover
30637              * Fires when the mouse hovers over the button
30638              * @param {Button} this
30639              * @param {Event} e The event object
30640              */
30641         'mouseover' : true,
30642         /**
30643              * @event mouseout
30644              * Fires when the mouse exits the button
30645              * @param {Button} this
30646              * @param {Event} e The event object
30647              */
30648         'mouseout': true,
30649          /**
30650              * @event render
30651              * Fires when the button is rendered
30652              * @param {Button} this
30653              */
30654         'render': true
30655     });
30656     if(this.menu){
30657         this.menu = Roo.menu.MenuMgr.get(this.menu);
30658     }
30659     // register listeners first!!  - so render can be captured..
30660     Roo.util.Observable.call(this);
30661     if(renderTo){
30662         this.render(renderTo);
30663     }
30664     
30665   
30666 };
30667
30668 Roo.extend(Roo.Button, Roo.util.Observable, {
30669     /**
30670      * 
30671      */
30672     
30673     /**
30674      * Read-only. True if this button is hidden
30675      * @type Boolean
30676      */
30677     hidden : false,
30678     /**
30679      * Read-only. True if this button is disabled
30680      * @type Boolean
30681      */
30682     disabled : false,
30683     /**
30684      * Read-only. True if this button is pressed (only if enableToggle = true)
30685      * @type Boolean
30686      */
30687     pressed : false,
30688
30689     /**
30690      * @cfg {Number} tabIndex 
30691      * The DOM tabIndex for this button (defaults to undefined)
30692      */
30693     tabIndex : undefined,
30694
30695     /**
30696      * @cfg {Boolean} enableToggle
30697      * True to enable pressed/not pressed toggling (defaults to false)
30698      */
30699     enableToggle: false,
30700     /**
30701      * @cfg {Roo.menu.Menu} menu
30702      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30703      */
30704     menu : undefined,
30705     /**
30706      * @cfg {String} menuAlign
30707      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30708      */
30709     menuAlign : "tl-bl?",
30710
30711     /**
30712      * @cfg {String} iconCls
30713      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30714      */
30715     iconCls : undefined,
30716     /**
30717      * @cfg {String} type
30718      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30719      */
30720     type : 'button',
30721
30722     // private
30723     menuClassTarget: 'tr',
30724
30725     /**
30726      * @cfg {String} clickEvent
30727      * The type of event to map to the button's event handler (defaults to 'click')
30728      */
30729     clickEvent : 'click',
30730
30731     /**
30732      * @cfg {Boolean} handleMouseEvents
30733      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30734      */
30735     handleMouseEvents : true,
30736
30737     /**
30738      * @cfg {String} tooltipType
30739      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30740      */
30741     tooltipType : 'qtip',
30742
30743     /**
30744      * @cfg {String} cls
30745      * A CSS class to apply to the button's main element.
30746      */
30747     
30748     /**
30749      * @cfg {Roo.Template} template (Optional)
30750      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30751      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30752      * require code modifications if required elements (e.g. a button) aren't present.
30753      */
30754
30755     // private
30756     render : function(renderTo){
30757         var btn;
30758         if(this.hideParent){
30759             this.parentEl = Roo.get(renderTo);
30760         }
30761         if(!this.dhconfig){
30762             if(!this.template){
30763                 if(!Roo.Button.buttonTemplate){
30764                     // hideous table template
30765                     Roo.Button.buttonTemplate = new Roo.Template(
30766                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30767                         '<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>',
30768                         "</tr></tbody></table>");
30769                 }
30770                 this.template = Roo.Button.buttonTemplate;
30771             }
30772             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30773             var btnEl = btn.child("button:first");
30774             btnEl.on('focus', this.onFocus, this);
30775             btnEl.on('blur', this.onBlur, this);
30776             if(this.cls){
30777                 btn.addClass(this.cls);
30778             }
30779             if(this.icon){
30780                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30781             }
30782             if(this.iconCls){
30783                 btnEl.addClass(this.iconCls);
30784                 if(!this.cls){
30785                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30786                 }
30787             }
30788             if(this.tabIndex !== undefined){
30789                 btnEl.dom.tabIndex = this.tabIndex;
30790             }
30791             if(this.tooltip){
30792                 if(typeof this.tooltip == 'object'){
30793                     Roo.QuickTips.tips(Roo.apply({
30794                           target: btnEl.id
30795                     }, this.tooltip));
30796                 } else {
30797                     btnEl.dom[this.tooltipType] = this.tooltip;
30798                 }
30799             }
30800         }else{
30801             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30802         }
30803         this.el = btn;
30804         if(this.id){
30805             this.el.dom.id = this.el.id = this.id;
30806         }
30807         if(this.menu){
30808             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30809             this.menu.on("show", this.onMenuShow, this);
30810             this.menu.on("hide", this.onMenuHide, this);
30811         }
30812         btn.addClass("x-btn");
30813         if(Roo.isIE && !Roo.isIE7){
30814             this.autoWidth.defer(1, this);
30815         }else{
30816             this.autoWidth();
30817         }
30818         if(this.handleMouseEvents){
30819             btn.on("mouseover", this.onMouseOver, this);
30820             btn.on("mouseout", this.onMouseOut, this);
30821             btn.on("mousedown", this.onMouseDown, this);
30822         }
30823         btn.on(this.clickEvent, this.onClick, this);
30824         //btn.on("mouseup", this.onMouseUp, this);
30825         if(this.hidden){
30826             this.hide();
30827         }
30828         if(this.disabled){
30829             this.disable();
30830         }
30831         Roo.ButtonToggleMgr.register(this);
30832         if(this.pressed){
30833             this.el.addClass("x-btn-pressed");
30834         }
30835         if(this.repeat){
30836             var repeater = new Roo.util.ClickRepeater(btn,
30837                 typeof this.repeat == "object" ? this.repeat : {}
30838             );
30839             repeater.on("click", this.onClick,  this);
30840         }
30841         
30842         this.fireEvent('render', this);
30843         
30844     },
30845     /**
30846      * Returns the button's underlying element
30847      * @return {Roo.Element} The element
30848      */
30849     getEl : function(){
30850         return this.el;  
30851     },
30852     
30853     /**
30854      * Destroys this Button and removes any listeners.
30855      */
30856     destroy : function(){
30857         Roo.ButtonToggleMgr.unregister(this);
30858         this.el.removeAllListeners();
30859         this.purgeListeners();
30860         this.el.remove();
30861     },
30862
30863     // private
30864     autoWidth : function(){
30865         if(this.el){
30866             this.el.setWidth("auto");
30867             if(Roo.isIE7 && Roo.isStrict){
30868                 var ib = this.el.child('button');
30869                 if(ib && ib.getWidth() > 20){
30870                     ib.clip();
30871                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30872                 }
30873             }
30874             if(this.minWidth){
30875                 if(this.hidden){
30876                     this.el.beginMeasure();
30877                 }
30878                 if(this.el.getWidth() < this.minWidth){
30879                     this.el.setWidth(this.minWidth);
30880                 }
30881                 if(this.hidden){
30882                     this.el.endMeasure();
30883                 }
30884             }
30885         }
30886     },
30887
30888     /**
30889      * Assigns this button's click handler
30890      * @param {Function} handler The function to call when the button is clicked
30891      * @param {Object} scope (optional) Scope for the function passed in
30892      */
30893     setHandler : function(handler, scope){
30894         this.handler = handler;
30895         this.scope = scope;  
30896     },
30897     
30898     /**
30899      * Sets this button's text
30900      * @param {String} text The button text
30901      */
30902     setText : function(text){
30903         this.text = text;
30904         if(this.el){
30905             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30906         }
30907         this.autoWidth();
30908     },
30909     
30910     /**
30911      * Gets the text for this button
30912      * @return {String} The button text
30913      */
30914     getText : function(){
30915         return this.text;  
30916     },
30917     
30918     /**
30919      * Show this button
30920      */
30921     show: function(){
30922         this.hidden = false;
30923         if(this.el){
30924             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30925         }
30926     },
30927     
30928     /**
30929      * Hide this button
30930      */
30931     hide: function(){
30932         this.hidden = true;
30933         if(this.el){
30934             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30935         }
30936     },
30937     
30938     /**
30939      * Convenience function for boolean show/hide
30940      * @param {Boolean} visible True to show, false to hide
30941      */
30942     setVisible: function(visible){
30943         if(visible) {
30944             this.show();
30945         }else{
30946             this.hide();
30947         }
30948     },
30949     
30950     /**
30951      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30952      * @param {Boolean} state (optional) Force a particular state
30953      */
30954     toggle : function(state){
30955         state = state === undefined ? !this.pressed : state;
30956         if(state != this.pressed){
30957             if(state){
30958                 this.el.addClass("x-btn-pressed");
30959                 this.pressed = true;
30960                 this.fireEvent("toggle", this, true);
30961             }else{
30962                 this.el.removeClass("x-btn-pressed");
30963                 this.pressed = false;
30964                 this.fireEvent("toggle", this, false);
30965             }
30966             if(this.toggleHandler){
30967                 this.toggleHandler.call(this.scope || this, this, state);
30968             }
30969         }
30970     },
30971     
30972     /**
30973      * Focus the button
30974      */
30975     focus : function(){
30976         this.el.child('button:first').focus();
30977     },
30978     
30979     /**
30980      * Disable this button
30981      */
30982     disable : function(){
30983         if(this.el){
30984             this.el.addClass("x-btn-disabled");
30985         }
30986         this.disabled = true;
30987     },
30988     
30989     /**
30990      * Enable this button
30991      */
30992     enable : function(){
30993         if(this.el){
30994             this.el.removeClass("x-btn-disabled");
30995         }
30996         this.disabled = false;
30997     },
30998
30999     /**
31000      * Convenience function for boolean enable/disable
31001      * @param {Boolean} enabled True to enable, false to disable
31002      */
31003     setDisabled : function(v){
31004         this[v !== true ? "enable" : "disable"]();
31005     },
31006
31007     // private
31008     onClick : function(e)
31009     {
31010         if(e){
31011             e.preventDefault();
31012         }
31013         if(e.button != 0){
31014             return;
31015         }
31016         if(!this.disabled){
31017             if(this.enableToggle){
31018                 this.toggle();
31019             }
31020             if(this.menu && !this.menu.isVisible()){
31021                 this.menu.show(this.el, this.menuAlign);
31022             }
31023             this.fireEvent("click", this, e);
31024             if(this.handler){
31025                 this.el.removeClass("x-btn-over");
31026                 this.handler.call(this.scope || this, this, e);
31027             }
31028         }
31029     },
31030     // private
31031     onMouseOver : function(e){
31032         if(!this.disabled){
31033             this.el.addClass("x-btn-over");
31034             this.fireEvent('mouseover', this, e);
31035         }
31036     },
31037     // private
31038     onMouseOut : function(e){
31039         if(!e.within(this.el,  true)){
31040             this.el.removeClass("x-btn-over");
31041             this.fireEvent('mouseout', this, e);
31042         }
31043     },
31044     // private
31045     onFocus : function(e){
31046         if(!this.disabled){
31047             this.el.addClass("x-btn-focus");
31048         }
31049     },
31050     // private
31051     onBlur : function(e){
31052         this.el.removeClass("x-btn-focus");
31053     },
31054     // private
31055     onMouseDown : function(e){
31056         if(!this.disabled && e.button == 0){
31057             this.el.addClass("x-btn-click");
31058             Roo.get(document).on('mouseup', this.onMouseUp, this);
31059         }
31060     },
31061     // private
31062     onMouseUp : function(e){
31063         if(e.button == 0){
31064             this.el.removeClass("x-btn-click");
31065             Roo.get(document).un('mouseup', this.onMouseUp, this);
31066         }
31067     },
31068     // private
31069     onMenuShow : function(e){
31070         this.el.addClass("x-btn-menu-active");
31071     },
31072     // private
31073     onMenuHide : function(e){
31074         this.el.removeClass("x-btn-menu-active");
31075     }   
31076 });
31077
31078 // Private utility class used by Button
31079 Roo.ButtonToggleMgr = function(){
31080    var groups = {};
31081    
31082    function toggleGroup(btn, state){
31083        if(state){
31084            var g = groups[btn.toggleGroup];
31085            for(var i = 0, l = g.length; i < l; i++){
31086                if(g[i] != btn){
31087                    g[i].toggle(false);
31088                }
31089            }
31090        }
31091    }
31092    
31093    return {
31094        register : function(btn){
31095            if(!btn.toggleGroup){
31096                return;
31097            }
31098            var g = groups[btn.toggleGroup];
31099            if(!g){
31100                g = groups[btn.toggleGroup] = [];
31101            }
31102            g.push(btn);
31103            btn.on("toggle", toggleGroup);
31104        },
31105        
31106        unregister : function(btn){
31107            if(!btn.toggleGroup){
31108                return;
31109            }
31110            var g = groups[btn.toggleGroup];
31111            if(g){
31112                g.remove(btn);
31113                btn.un("toggle", toggleGroup);
31114            }
31115        }
31116    };
31117 }();/*
31118  * Based on:
31119  * Ext JS Library 1.1.1
31120  * Copyright(c) 2006-2007, Ext JS, LLC.
31121  *
31122  * Originally Released Under LGPL - original licence link has changed is not relivant.
31123  *
31124  * Fork - LGPL
31125  * <script type="text/javascript">
31126  */
31127  
31128 /**
31129  * @class Roo.SplitButton
31130  * @extends Roo.Button
31131  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
31132  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
31133  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
31134  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
31135  * @cfg {String} arrowTooltip The title attribute of the arrow
31136  * @constructor
31137  * Create a new menu button
31138  * @param {String/HTMLElement/Element} renderTo The element to append the button to
31139  * @param {Object} config The config object
31140  */
31141 Roo.SplitButton = function(renderTo, config){
31142     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
31143     /**
31144      * @event arrowclick
31145      * Fires when this button's arrow is clicked
31146      * @param {SplitButton} this
31147      * @param {EventObject} e The click event
31148      */
31149     this.addEvents({"arrowclick":true});
31150 };
31151
31152 Roo.extend(Roo.SplitButton, Roo.Button, {
31153     render : function(renderTo){
31154         // this is one sweet looking template!
31155         var tpl = new Roo.Template(
31156             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
31157             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
31158             '<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>',
31159             "</tbody></table></td><td>",
31160             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
31161             '<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>',
31162             "</tbody></table></td></tr></table>"
31163         );
31164         var btn = tpl.append(renderTo, [this.text, this.type], true);
31165         var btnEl = btn.child("button");
31166         if(this.cls){
31167             btn.addClass(this.cls);
31168         }
31169         if(this.icon){
31170             btnEl.setStyle('background-image', 'url(' +this.icon +')');
31171         }
31172         if(this.iconCls){
31173             btnEl.addClass(this.iconCls);
31174             if(!this.cls){
31175                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
31176             }
31177         }
31178         this.el = btn;
31179         if(this.handleMouseEvents){
31180             btn.on("mouseover", this.onMouseOver, this);
31181             btn.on("mouseout", this.onMouseOut, this);
31182             btn.on("mousedown", this.onMouseDown, this);
31183             btn.on("mouseup", this.onMouseUp, this);
31184         }
31185         btn.on(this.clickEvent, this.onClick, this);
31186         if(this.tooltip){
31187             if(typeof this.tooltip == 'object'){
31188                 Roo.QuickTips.tips(Roo.apply({
31189                       target: btnEl.id
31190                 }, this.tooltip));
31191             } else {
31192                 btnEl.dom[this.tooltipType] = this.tooltip;
31193             }
31194         }
31195         if(this.arrowTooltip){
31196             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
31197         }
31198         if(this.hidden){
31199             this.hide();
31200         }
31201         if(this.disabled){
31202             this.disable();
31203         }
31204         if(this.pressed){
31205             this.el.addClass("x-btn-pressed");
31206         }
31207         if(Roo.isIE && !Roo.isIE7){
31208             this.autoWidth.defer(1, this);
31209         }else{
31210             this.autoWidth();
31211         }
31212         if(this.menu){
31213             this.menu.on("show", this.onMenuShow, this);
31214             this.menu.on("hide", this.onMenuHide, this);
31215         }
31216         this.fireEvent('render', this);
31217     },
31218
31219     // private
31220     autoWidth : function(){
31221         if(this.el){
31222             var tbl = this.el.child("table:first");
31223             var tbl2 = this.el.child("table:last");
31224             this.el.setWidth("auto");
31225             tbl.setWidth("auto");
31226             if(Roo.isIE7 && Roo.isStrict){
31227                 var ib = this.el.child('button:first');
31228                 if(ib && ib.getWidth() > 20){
31229                     ib.clip();
31230                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31231                 }
31232             }
31233             if(this.minWidth){
31234                 if(this.hidden){
31235                     this.el.beginMeasure();
31236                 }
31237                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
31238                     tbl.setWidth(this.minWidth-tbl2.getWidth());
31239                 }
31240                 if(this.hidden){
31241                     this.el.endMeasure();
31242                 }
31243             }
31244             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
31245         } 
31246     },
31247     /**
31248      * Sets this button's click handler
31249      * @param {Function} handler The function to call when the button is clicked
31250      * @param {Object} scope (optional) Scope for the function passed above
31251      */
31252     setHandler : function(handler, scope){
31253         this.handler = handler;
31254         this.scope = scope;  
31255     },
31256     
31257     /**
31258      * Sets this button's arrow click handler
31259      * @param {Function} handler The function to call when the arrow is clicked
31260      * @param {Object} scope (optional) Scope for the function passed above
31261      */
31262     setArrowHandler : function(handler, scope){
31263         this.arrowHandler = handler;
31264         this.scope = scope;  
31265     },
31266     
31267     /**
31268      * Focus the button
31269      */
31270     focus : function(){
31271         if(this.el){
31272             this.el.child("button:first").focus();
31273         }
31274     },
31275
31276     // private
31277     onClick : function(e){
31278         e.preventDefault();
31279         if(!this.disabled){
31280             if(e.getTarget(".x-btn-menu-arrow-wrap")){
31281                 if(this.menu && !this.menu.isVisible()){
31282                     this.menu.show(this.el, this.menuAlign);
31283                 }
31284                 this.fireEvent("arrowclick", this, e);
31285                 if(this.arrowHandler){
31286                     this.arrowHandler.call(this.scope || this, this, e);
31287                 }
31288             }else{
31289                 this.fireEvent("click", this, e);
31290                 if(this.handler){
31291                     this.handler.call(this.scope || this, this, e);
31292                 }
31293             }
31294         }
31295     },
31296     // private
31297     onMouseDown : function(e){
31298         if(!this.disabled){
31299             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
31300         }
31301     },
31302     // private
31303     onMouseUp : function(e){
31304         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
31305     }   
31306 });
31307
31308
31309 // backwards compat
31310 Roo.MenuButton = Roo.SplitButton;/*
31311  * Based on:
31312  * Ext JS Library 1.1.1
31313  * Copyright(c) 2006-2007, Ext JS, LLC.
31314  *
31315  * Originally Released Under LGPL - original licence link has changed is not relivant.
31316  *
31317  * Fork - LGPL
31318  * <script type="text/javascript">
31319  */
31320
31321 /**
31322  * @class Roo.Toolbar
31323  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
31324  * Basic Toolbar class.
31325  * @constructor
31326  * Creates a new Toolbar
31327  * @param {Object} container The config object
31328  */ 
31329 Roo.Toolbar = function(container, buttons, config)
31330 {
31331     /// old consturctor format still supported..
31332     if(container instanceof Array){ // omit the container for later rendering
31333         buttons = container;
31334         config = buttons;
31335         container = null;
31336     }
31337     if (typeof(container) == 'object' && container.xtype) {
31338         config = container;
31339         container = config.container;
31340         buttons = config.buttons || []; // not really - use items!!
31341     }
31342     var xitems = [];
31343     if (config && config.items) {
31344         xitems = config.items;
31345         delete config.items;
31346     }
31347     Roo.apply(this, config);
31348     this.buttons = buttons;
31349     
31350     if(container){
31351         this.render(container);
31352     }
31353     this.xitems = xitems;
31354     Roo.each(xitems, function(b) {
31355         this.add(b);
31356     }, this);
31357     
31358 };
31359
31360 Roo.Toolbar.prototype = {
31361     /**
31362      * @cfg {Array} items
31363      * array of button configs or elements to add (will be converted to a MixedCollection)
31364      */
31365     items: false,
31366     /**
31367      * @cfg {String/HTMLElement/Element} container
31368      * The id or element that will contain the toolbar
31369      */
31370     // private
31371     render : function(ct){
31372         this.el = Roo.get(ct);
31373         if(this.cls){
31374             this.el.addClass(this.cls);
31375         }
31376         // using a table allows for vertical alignment
31377         // 100% width is needed by Safari...
31378         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31379         this.tr = this.el.child("tr", true);
31380         var autoId = 0;
31381         this.items = new Roo.util.MixedCollection(false, function(o){
31382             return o.id || ("item" + (++autoId));
31383         });
31384         if(this.buttons){
31385             this.add.apply(this, this.buttons);
31386             delete this.buttons;
31387         }
31388     },
31389
31390     /**
31391      * Adds element(s) to the toolbar -- this function takes a variable number of 
31392      * arguments of mixed type and adds them to the toolbar.
31393      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31394      * <ul>
31395      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31396      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31397      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31398      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31399      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31400      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31401      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31402      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31403      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31404      * </ul>
31405      * @param {Mixed} arg2
31406      * @param {Mixed} etc.
31407      */
31408     add : function(){
31409         var a = arguments, l = a.length;
31410         for(var i = 0; i < l; i++){
31411             this._add(a[i]);
31412         }
31413     },
31414     // private..
31415     _add : function(el) {
31416         
31417         if (el.xtype) {
31418             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31419         }
31420         
31421         if (el.applyTo){ // some kind of form field
31422             return this.addField(el);
31423         } 
31424         if (el.render){ // some kind of Toolbar.Item
31425             return this.addItem(el);
31426         }
31427         if (typeof el == "string"){ // string
31428             if(el == "separator" || el == "-"){
31429                 return this.addSeparator();
31430             }
31431             if (el == " "){
31432                 return this.addSpacer();
31433             }
31434             if(el == "->"){
31435                 return this.addFill();
31436             }
31437             return this.addText(el);
31438             
31439         }
31440         if(el.tagName){ // element
31441             return this.addElement(el);
31442         }
31443         if(typeof el == "object"){ // must be button config?
31444             return this.addButton(el);
31445         }
31446         // and now what?!?!
31447         return false;
31448         
31449     },
31450     
31451     /**
31452      * Add an Xtype element
31453      * @param {Object} xtype Xtype Object
31454      * @return {Object} created Object
31455      */
31456     addxtype : function(e){
31457         return this.add(e);  
31458     },
31459     
31460     /**
31461      * Returns the Element for this toolbar.
31462      * @return {Roo.Element}
31463      */
31464     getEl : function(){
31465         return this.el;  
31466     },
31467     
31468     /**
31469      * Adds a separator
31470      * @return {Roo.Toolbar.Item} The separator item
31471      */
31472     addSeparator : function(){
31473         return this.addItem(new Roo.Toolbar.Separator());
31474     },
31475
31476     /**
31477      * Adds a spacer element
31478      * @return {Roo.Toolbar.Spacer} The spacer item
31479      */
31480     addSpacer : function(){
31481         return this.addItem(new Roo.Toolbar.Spacer());
31482     },
31483
31484     /**
31485      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31486      * @return {Roo.Toolbar.Fill} The fill item
31487      */
31488     addFill : function(){
31489         return this.addItem(new Roo.Toolbar.Fill());
31490     },
31491
31492     /**
31493      * Adds any standard HTML element to the toolbar
31494      * @param {String/HTMLElement/Element} el The element or id of the element to add
31495      * @return {Roo.Toolbar.Item} The element's item
31496      */
31497     addElement : function(el){
31498         return this.addItem(new Roo.Toolbar.Item(el));
31499     },
31500     /**
31501      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31502      * @type Roo.util.MixedCollection  
31503      */
31504     items : false,
31505      
31506     /**
31507      * Adds any Toolbar.Item or subclass
31508      * @param {Roo.Toolbar.Item} item
31509      * @return {Roo.Toolbar.Item} The item
31510      */
31511     addItem : function(item){
31512         var td = this.nextBlock();
31513         item.render(td);
31514         this.items.add(item);
31515         return item;
31516     },
31517     
31518     /**
31519      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31520      * @param {Object/Array} config A button config or array of configs
31521      * @return {Roo.Toolbar.Button/Array}
31522      */
31523     addButton : function(config){
31524         if(config instanceof Array){
31525             var buttons = [];
31526             for(var i = 0, len = config.length; i < len; i++) {
31527                 buttons.push(this.addButton(config[i]));
31528             }
31529             return buttons;
31530         }
31531         var b = config;
31532         if(!(config instanceof Roo.Toolbar.Button)){
31533             b = config.split ?
31534                 new Roo.Toolbar.SplitButton(config) :
31535                 new Roo.Toolbar.Button(config);
31536         }
31537         var td = this.nextBlock();
31538         b.render(td);
31539         this.items.add(b);
31540         return b;
31541     },
31542     
31543     /**
31544      * Adds text to the toolbar
31545      * @param {String} text The text to add
31546      * @return {Roo.Toolbar.Item} The element's item
31547      */
31548     addText : function(text){
31549         return this.addItem(new Roo.Toolbar.TextItem(text));
31550     },
31551     
31552     /**
31553      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31554      * @param {Number} index The index where the item is to be inserted
31555      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31556      * @return {Roo.Toolbar.Button/Item}
31557      */
31558     insertButton : function(index, item){
31559         if(item instanceof Array){
31560             var buttons = [];
31561             for(var i = 0, len = item.length; i < len; i++) {
31562                buttons.push(this.insertButton(index + i, item[i]));
31563             }
31564             return buttons;
31565         }
31566         if (!(item instanceof Roo.Toolbar.Button)){
31567            item = new Roo.Toolbar.Button(item);
31568         }
31569         var td = document.createElement("td");
31570         this.tr.insertBefore(td, this.tr.childNodes[index]);
31571         item.render(td);
31572         this.items.insert(index, item);
31573         return item;
31574     },
31575     
31576     /**
31577      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31578      * @param {Object} config
31579      * @return {Roo.Toolbar.Item} The element's item
31580      */
31581     addDom : function(config, returnEl){
31582         var td = this.nextBlock();
31583         Roo.DomHelper.overwrite(td, config);
31584         var ti = new Roo.Toolbar.Item(td.firstChild);
31585         ti.render(td);
31586         this.items.add(ti);
31587         return ti;
31588     },
31589
31590     /**
31591      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31592      * @type Roo.util.MixedCollection  
31593      */
31594     fields : false,
31595     
31596     /**
31597      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31598      * Note: the field should not have been rendered yet. For a field that has already been
31599      * rendered, use {@link #addElement}.
31600      * @param {Roo.form.Field} field
31601      * @return {Roo.ToolbarItem}
31602      */
31603      
31604       
31605     addField : function(field) {
31606         if (!this.fields) {
31607             var autoId = 0;
31608             this.fields = new Roo.util.MixedCollection(false, function(o){
31609                 return o.id || ("item" + (++autoId));
31610             });
31611
31612         }
31613         
31614         var td = this.nextBlock();
31615         field.render(td);
31616         var ti = new Roo.Toolbar.Item(td.firstChild);
31617         ti.render(td);
31618         this.items.add(ti);
31619         this.fields.add(field);
31620         return ti;
31621     },
31622     /**
31623      * Hide the toolbar
31624      * @method hide
31625      */
31626      
31627       
31628     hide : function()
31629     {
31630         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31631         this.el.child('div').hide();
31632     },
31633     /**
31634      * Show the toolbar
31635      * @method show
31636      */
31637     show : function()
31638     {
31639         this.el.child('div').show();
31640     },
31641       
31642     // private
31643     nextBlock : function(){
31644         var td = document.createElement("td");
31645         this.tr.appendChild(td);
31646         return td;
31647     },
31648
31649     // private
31650     destroy : function(){
31651         if(this.items){ // rendered?
31652             Roo.destroy.apply(Roo, this.items.items);
31653         }
31654         if(this.fields){ // rendered?
31655             Roo.destroy.apply(Roo, this.fields.items);
31656         }
31657         Roo.Element.uncache(this.el, this.tr);
31658     }
31659 };
31660
31661 /**
31662  * @class Roo.Toolbar.Item
31663  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31664  * @constructor
31665  * Creates a new Item
31666  * @param {HTMLElement} el 
31667  */
31668 Roo.Toolbar.Item = function(el){
31669     var cfg = {};
31670     if (typeof (el.xtype) != 'undefined') {
31671         cfg = el;
31672         el = cfg.el;
31673     }
31674     
31675     this.el = Roo.getDom(el);
31676     this.id = Roo.id(this.el);
31677     this.hidden = false;
31678     
31679     this.addEvents({
31680          /**
31681              * @event render
31682              * Fires when the button is rendered
31683              * @param {Button} this
31684              */
31685         'render': true
31686     });
31687     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31688 };
31689 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31690 //Roo.Toolbar.Item.prototype = {
31691     
31692     /**
31693      * Get this item's HTML Element
31694      * @return {HTMLElement}
31695      */
31696     getEl : function(){
31697        return this.el;  
31698     },
31699
31700     // private
31701     render : function(td){
31702         
31703          this.td = td;
31704         td.appendChild(this.el);
31705         
31706         this.fireEvent('render', this);
31707     },
31708     
31709     /**
31710      * Removes and destroys this item.
31711      */
31712     destroy : function(){
31713         this.td.parentNode.removeChild(this.td);
31714     },
31715     
31716     /**
31717      * Shows this item.
31718      */
31719     show: function(){
31720         this.hidden = false;
31721         this.td.style.display = "";
31722     },
31723     
31724     /**
31725      * Hides this item.
31726      */
31727     hide: function(){
31728         this.hidden = true;
31729         this.td.style.display = "none";
31730     },
31731     
31732     /**
31733      * Convenience function for boolean show/hide.
31734      * @param {Boolean} visible true to show/false to hide
31735      */
31736     setVisible: function(visible){
31737         if(visible) {
31738             this.show();
31739         }else{
31740             this.hide();
31741         }
31742     },
31743     
31744     /**
31745      * Try to focus this item.
31746      */
31747     focus : function(){
31748         Roo.fly(this.el).focus();
31749     },
31750     
31751     /**
31752      * Disables this item.
31753      */
31754     disable : function(){
31755         Roo.fly(this.td).addClass("x-item-disabled");
31756         this.disabled = true;
31757         this.el.disabled = true;
31758     },
31759     
31760     /**
31761      * Enables this item.
31762      */
31763     enable : function(){
31764         Roo.fly(this.td).removeClass("x-item-disabled");
31765         this.disabled = false;
31766         this.el.disabled = false;
31767     }
31768 });
31769
31770
31771 /**
31772  * @class Roo.Toolbar.Separator
31773  * @extends Roo.Toolbar.Item
31774  * A simple toolbar separator class
31775  * @constructor
31776  * Creates a new Separator
31777  */
31778 Roo.Toolbar.Separator = function(cfg){
31779     
31780     var s = document.createElement("span");
31781     s.className = "ytb-sep";
31782     if (cfg) {
31783         cfg.el = s;
31784     }
31785     
31786     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31787 };
31788 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31789     enable:Roo.emptyFn,
31790     disable:Roo.emptyFn,
31791     focus:Roo.emptyFn
31792 });
31793
31794 /**
31795  * @class Roo.Toolbar.Spacer
31796  * @extends Roo.Toolbar.Item
31797  * A simple element that adds extra horizontal space to a toolbar.
31798  * @constructor
31799  * Creates a new Spacer
31800  */
31801 Roo.Toolbar.Spacer = function(cfg){
31802     var s = document.createElement("div");
31803     s.className = "ytb-spacer";
31804     if (cfg) {
31805         cfg.el = s;
31806     }
31807     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31808 };
31809 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31810     enable:Roo.emptyFn,
31811     disable:Roo.emptyFn,
31812     focus:Roo.emptyFn
31813 });
31814
31815 /**
31816  * @class Roo.Toolbar.Fill
31817  * @extends Roo.Toolbar.Spacer
31818  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31819  * @constructor
31820  * Creates a new Spacer
31821  */
31822 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31823     // private
31824     render : function(td){
31825         td.style.width = '100%';
31826         Roo.Toolbar.Fill.superclass.render.call(this, td);
31827     }
31828 });
31829
31830 /**
31831  * @class Roo.Toolbar.TextItem
31832  * @extends Roo.Toolbar.Item
31833  * A simple class that renders text directly into a toolbar.
31834  * @constructor
31835  * Creates a new TextItem
31836  * @cfg {string} text 
31837  */
31838 Roo.Toolbar.TextItem = function(cfg){
31839     var  text = cfg || "";
31840     if (typeof(cfg) == 'object') {
31841         text = cfg.text || "";
31842     }  else {
31843         cfg = null;
31844     }
31845     var s = document.createElement("span");
31846     s.className = "ytb-text";
31847     s.innerHTML = text;
31848     if (cfg) {
31849         cfg.el  = s;
31850     }
31851     
31852     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31853 };
31854 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31855     
31856      
31857     enable:Roo.emptyFn,
31858     disable:Roo.emptyFn,
31859     focus:Roo.emptyFn,
31860      /**
31861      * Shows this button
31862      */
31863     show: function(){
31864         this.hidden = false;
31865         this.el.style.display = "";
31866     },
31867     
31868     /**
31869      * Hides this button
31870      */
31871     hide: function(){
31872         this.hidden = true;
31873         this.el.style.display = "none";
31874     }
31875     
31876 });
31877
31878 /**
31879  * @class Roo.Toolbar.Button
31880  * @extends Roo.Button
31881  * A button that renders into a toolbar.
31882  * @constructor
31883  * Creates a new Button
31884  * @param {Object} config A standard {@link Roo.Button} config object
31885  */
31886 Roo.Toolbar.Button = function(config){
31887     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31888 };
31889 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31890 {
31891     
31892     
31893     render : function(td){
31894         this.td = td;
31895         Roo.Toolbar.Button.superclass.render.call(this, td);
31896     },
31897     
31898     /**
31899      * Removes and destroys this button
31900      */
31901     destroy : function(){
31902         Roo.Toolbar.Button.superclass.destroy.call(this);
31903         this.td.parentNode.removeChild(this.td);
31904     },
31905     
31906     /**
31907      * Shows this button
31908      */
31909     show: function(){
31910         this.hidden = false;
31911         this.td.style.display = "";
31912     },
31913     
31914     /**
31915      * Hides this button
31916      */
31917     hide: function(){
31918         this.hidden = true;
31919         this.td.style.display = "none";
31920     },
31921
31922     /**
31923      * Disables this item
31924      */
31925     disable : function(){
31926         Roo.fly(this.td).addClass("x-item-disabled");
31927         this.disabled = true;
31928     },
31929
31930     /**
31931      * Enables this item
31932      */
31933     enable : function(){
31934         Roo.fly(this.td).removeClass("x-item-disabled");
31935         this.disabled = false;
31936     }
31937 });
31938 // backwards compat
31939 Roo.ToolbarButton = Roo.Toolbar.Button;
31940
31941 /**
31942  * @class Roo.Toolbar.SplitButton
31943  * @extends Roo.SplitButton
31944  * A menu button that renders into a toolbar.
31945  * @constructor
31946  * Creates a new SplitButton
31947  * @param {Object} config A standard {@link Roo.SplitButton} config object
31948  */
31949 Roo.Toolbar.SplitButton = function(config){
31950     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31951 };
31952 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31953     render : function(td){
31954         this.td = td;
31955         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31956     },
31957     
31958     /**
31959      * Removes and destroys this button
31960      */
31961     destroy : function(){
31962         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31963         this.td.parentNode.removeChild(this.td);
31964     },
31965     
31966     /**
31967      * Shows this button
31968      */
31969     show: function(){
31970         this.hidden = false;
31971         this.td.style.display = "";
31972     },
31973     
31974     /**
31975      * Hides this button
31976      */
31977     hide: function(){
31978         this.hidden = true;
31979         this.td.style.display = "none";
31980     }
31981 });
31982
31983 // backwards compat
31984 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31985  * Based on:
31986  * Ext JS Library 1.1.1
31987  * Copyright(c) 2006-2007, Ext JS, LLC.
31988  *
31989  * Originally Released Under LGPL - original licence link has changed is not relivant.
31990  *
31991  * Fork - LGPL
31992  * <script type="text/javascript">
31993  */
31994  
31995 /**
31996  * @class Roo.PagingToolbar
31997  * @extends Roo.Toolbar
31998  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
31999  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32000  * @constructor
32001  * Create a new PagingToolbar
32002  * @param {Object} config The config object
32003  */
32004 Roo.PagingToolbar = function(el, ds, config)
32005 {
32006     // old args format still supported... - xtype is prefered..
32007     if (typeof(el) == 'object' && el.xtype) {
32008         // created from xtype...
32009         config = el;
32010         ds = el.dataSource;
32011         el = config.container;
32012     }
32013     var items = [];
32014     if (config.items) {
32015         items = config.items;
32016         config.items = [];
32017     }
32018     
32019     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
32020     this.ds = ds;
32021     this.cursor = 0;
32022     this.renderButtons(this.el);
32023     this.bind(ds);
32024     
32025     // supprot items array.
32026    
32027     Roo.each(items, function(e) {
32028         this.add(Roo.factory(e));
32029     },this);
32030     
32031 };
32032
32033 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
32034    
32035     /**
32036      * @cfg {String/HTMLElement/Element} container
32037      * container The id or element that will contain the toolbar
32038      */
32039     /**
32040      * @cfg {Boolean} displayInfo
32041      * True to display the displayMsg (defaults to false)
32042      */
32043     
32044     
32045     /**
32046      * @cfg {Number} pageSize
32047      * The number of records to display per page (defaults to 20)
32048      */
32049     pageSize: 20,
32050     /**
32051      * @cfg {String} displayMsg
32052      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32053      */
32054     displayMsg : 'Displaying {0} - {1} of {2}',
32055     /**
32056      * @cfg {String} emptyMsg
32057      * The message to display when no records are found (defaults to "No data to display")
32058      */
32059     emptyMsg : 'No data to display',
32060     /**
32061      * Customizable piece of the default paging text (defaults to "Page")
32062      * @type String
32063      */
32064     beforePageText : "Page",
32065     /**
32066      * Customizable piece of the default paging text (defaults to "of %0")
32067      * @type String
32068      */
32069     afterPageText : "of {0}",
32070     /**
32071      * Customizable piece of the default paging text (defaults to "First Page")
32072      * @type String
32073      */
32074     firstText : "First Page",
32075     /**
32076      * Customizable piece of the default paging text (defaults to "Previous Page")
32077      * @type String
32078      */
32079     prevText : "Previous Page",
32080     /**
32081      * Customizable piece of the default paging text (defaults to "Next Page")
32082      * @type String
32083      */
32084     nextText : "Next Page",
32085     /**
32086      * Customizable piece of the default paging text (defaults to "Last Page")
32087      * @type String
32088      */
32089     lastText : "Last Page",
32090     /**
32091      * Customizable piece of the default paging text (defaults to "Refresh")
32092      * @type String
32093      */
32094     refreshText : "Refresh",
32095
32096     // private
32097     renderButtons : function(el){
32098         Roo.PagingToolbar.superclass.render.call(this, el);
32099         this.first = this.addButton({
32100             tooltip: this.firstText,
32101             cls: "x-btn-icon x-grid-page-first",
32102             disabled: true,
32103             handler: this.onClick.createDelegate(this, ["first"])
32104         });
32105         this.prev = this.addButton({
32106             tooltip: this.prevText,
32107             cls: "x-btn-icon x-grid-page-prev",
32108             disabled: true,
32109             handler: this.onClick.createDelegate(this, ["prev"])
32110         });
32111         //this.addSeparator();
32112         this.add(this.beforePageText);
32113         this.field = Roo.get(this.addDom({
32114            tag: "input",
32115            type: "text",
32116            size: "3",
32117            value: "1",
32118            cls: "x-grid-page-number"
32119         }).el);
32120         this.field.on("keydown", this.onPagingKeydown, this);
32121         this.field.on("focus", function(){this.dom.select();});
32122         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
32123         this.field.setHeight(18);
32124         //this.addSeparator();
32125         this.next = this.addButton({
32126             tooltip: this.nextText,
32127             cls: "x-btn-icon x-grid-page-next",
32128             disabled: true,
32129             handler: this.onClick.createDelegate(this, ["next"])
32130         });
32131         this.last = this.addButton({
32132             tooltip: this.lastText,
32133             cls: "x-btn-icon x-grid-page-last",
32134             disabled: true,
32135             handler: this.onClick.createDelegate(this, ["last"])
32136         });
32137         //this.addSeparator();
32138         this.loading = this.addButton({
32139             tooltip: this.refreshText,
32140             cls: "x-btn-icon x-grid-loading",
32141             handler: this.onClick.createDelegate(this, ["refresh"])
32142         });
32143
32144         if(this.displayInfo){
32145             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
32146         }
32147     },
32148
32149     // private
32150     updateInfo : function(){
32151         if(this.displayEl){
32152             var count = this.ds.getCount();
32153             var msg = count == 0 ?
32154                 this.emptyMsg :
32155                 String.format(
32156                     this.displayMsg,
32157                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32158                 );
32159             this.displayEl.update(msg);
32160         }
32161     },
32162
32163     // private
32164     onLoad : function(ds, r, o){
32165        this.cursor = o.params ? o.params.start : 0;
32166        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
32167
32168        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
32169        this.field.dom.value = ap;
32170        this.first.setDisabled(ap == 1);
32171        this.prev.setDisabled(ap == 1);
32172        this.next.setDisabled(ap == ps);
32173        this.last.setDisabled(ap == ps);
32174        this.loading.enable();
32175        this.updateInfo();
32176     },
32177
32178     // private
32179     getPageData : function(){
32180         var total = this.ds.getTotalCount();
32181         return {
32182             total : total,
32183             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32184             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32185         };
32186     },
32187
32188     // private
32189     onLoadError : function(){
32190         this.loading.enable();
32191     },
32192
32193     // private
32194     onPagingKeydown : function(e){
32195         var k = e.getKey();
32196         var d = this.getPageData();
32197         if(k == e.RETURN){
32198             var v = this.field.dom.value, pageNum;
32199             if(!v || isNaN(pageNum = parseInt(v, 10))){
32200                 this.field.dom.value = d.activePage;
32201                 return;
32202             }
32203             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32204             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32205             e.stopEvent();
32206         }
32207         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))
32208         {
32209           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32210           this.field.dom.value = pageNum;
32211           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32212           e.stopEvent();
32213         }
32214         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32215         {
32216           var v = this.field.dom.value, pageNum; 
32217           var increment = (e.shiftKey) ? 10 : 1;
32218           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32219             increment *= -1;
32220           }
32221           if(!v || isNaN(pageNum = parseInt(v, 10))) {
32222             this.field.dom.value = d.activePage;
32223             return;
32224           }
32225           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32226           {
32227             this.field.dom.value = parseInt(v, 10) + increment;
32228             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32229             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32230           }
32231           e.stopEvent();
32232         }
32233     },
32234
32235     // private
32236     beforeLoad : function(){
32237         if(this.loading){
32238             this.loading.disable();
32239         }
32240     },
32241
32242     // private
32243     onClick : function(which){
32244         var ds = this.ds;
32245         switch(which){
32246             case "first":
32247                 ds.load({params:{start: 0, limit: this.pageSize}});
32248             break;
32249             case "prev":
32250                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
32251             break;
32252             case "next":
32253                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
32254             break;
32255             case "last":
32256                 var total = ds.getTotalCount();
32257                 var extra = total % this.pageSize;
32258                 var lastStart = extra ? (total - extra) : total-this.pageSize;
32259                 ds.load({params:{start: lastStart, limit: this.pageSize}});
32260             break;
32261             case "refresh":
32262                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
32263             break;
32264         }
32265     },
32266
32267     /**
32268      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
32269      * @param {Roo.data.Store} store The data store to unbind
32270      */
32271     unbind : function(ds){
32272         ds.un("beforeload", this.beforeLoad, this);
32273         ds.un("load", this.onLoad, this);
32274         ds.un("loadexception", this.onLoadError, this);
32275         ds.un("remove", this.updateInfo, this);
32276         ds.un("add", this.updateInfo, this);
32277         this.ds = undefined;
32278     },
32279
32280     /**
32281      * Binds the paging toolbar to the specified {@link Roo.data.Store}
32282      * @param {Roo.data.Store} store The data store to bind
32283      */
32284     bind : function(ds){
32285         ds.on("beforeload", this.beforeLoad, this);
32286         ds.on("load", this.onLoad, this);
32287         ds.on("loadexception", this.onLoadError, this);
32288         ds.on("remove", this.updateInfo, this);
32289         ds.on("add", this.updateInfo, this);
32290         this.ds = ds;
32291     }
32292 });/*
32293  * Based on:
32294  * Ext JS Library 1.1.1
32295  * Copyright(c) 2006-2007, Ext JS, LLC.
32296  *
32297  * Originally Released Under LGPL - original licence link has changed is not relivant.
32298  *
32299  * Fork - LGPL
32300  * <script type="text/javascript">
32301  */
32302
32303 /**
32304  * @class Roo.Resizable
32305  * @extends Roo.util.Observable
32306  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
32307  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
32308  * 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
32309  * the element will be wrapped for you automatically.</p>
32310  * <p>Here is the list of valid resize handles:</p>
32311  * <pre>
32312 Value   Description
32313 ------  -------------------
32314  'n'     north
32315  's'     south
32316  'e'     east
32317  'w'     west
32318  'nw'    northwest
32319  'sw'    southwest
32320  'se'    southeast
32321  'ne'    northeast
32322  'hd'    horizontal drag
32323  'all'   all
32324 </pre>
32325  * <p>Here's an example showing the creation of a typical Resizable:</p>
32326  * <pre><code>
32327 var resizer = new Roo.Resizable("element-id", {
32328     handles: 'all',
32329     minWidth: 200,
32330     minHeight: 100,
32331     maxWidth: 500,
32332     maxHeight: 400,
32333     pinned: true
32334 });
32335 resizer.on("resize", myHandler);
32336 </code></pre>
32337  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32338  * resizer.east.setDisplayed(false);</p>
32339  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32340  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32341  * resize operation's new size (defaults to [0, 0])
32342  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32343  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32344  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32345  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32346  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32347  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32348  * @cfg {Number} width The width of the element in pixels (defaults to null)
32349  * @cfg {Number} height The height of the element in pixels (defaults to null)
32350  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32351  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32352  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32353  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32354  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
32355  * in favor of the handles config option (defaults to false)
32356  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32357  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32358  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32359  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32360  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32361  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32362  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32363  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32364  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32365  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32366  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32367  * @constructor
32368  * Create a new resizable component
32369  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32370  * @param {Object} config configuration options
32371   */
32372 Roo.Resizable = function(el, config)
32373 {
32374     this.el = Roo.get(el);
32375
32376     if(config && config.wrap){
32377         config.resizeChild = this.el;
32378         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32379         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32380         this.el.setStyle("overflow", "hidden");
32381         this.el.setPositioning(config.resizeChild.getPositioning());
32382         config.resizeChild.clearPositioning();
32383         if(!config.width || !config.height){
32384             var csize = config.resizeChild.getSize();
32385             this.el.setSize(csize.width, csize.height);
32386         }
32387         if(config.pinned && !config.adjustments){
32388             config.adjustments = "auto";
32389         }
32390     }
32391
32392     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32393     this.proxy.unselectable();
32394     this.proxy.enableDisplayMode('block');
32395
32396     Roo.apply(this, config);
32397
32398     if(this.pinned){
32399         this.disableTrackOver = true;
32400         this.el.addClass("x-resizable-pinned");
32401     }
32402     // if the element isn't positioned, make it relative
32403     var position = this.el.getStyle("position");
32404     if(position != "absolute" && position != "fixed"){
32405         this.el.setStyle("position", "relative");
32406     }
32407     if(!this.handles){ // no handles passed, must be legacy style
32408         this.handles = 's,e,se';
32409         if(this.multiDirectional){
32410             this.handles += ',n,w';
32411         }
32412     }
32413     if(this.handles == "all"){
32414         this.handles = "n s e w ne nw se sw";
32415     }
32416     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32417     var ps = Roo.Resizable.positions;
32418     for(var i = 0, len = hs.length; i < len; i++){
32419         if(hs[i] && ps[hs[i]]){
32420             var pos = ps[hs[i]];
32421             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32422         }
32423     }
32424     // legacy
32425     this.corner = this.southeast;
32426     
32427     // updateBox = the box can move..
32428     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32429         this.updateBox = true;
32430     }
32431
32432     this.activeHandle = null;
32433
32434     if(this.resizeChild){
32435         if(typeof this.resizeChild == "boolean"){
32436             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32437         }else{
32438             this.resizeChild = Roo.get(this.resizeChild, true);
32439         }
32440     }
32441     
32442     if(this.adjustments == "auto"){
32443         var rc = this.resizeChild;
32444         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32445         if(rc && (hw || hn)){
32446             rc.position("relative");
32447             rc.setLeft(hw ? hw.el.getWidth() : 0);
32448             rc.setTop(hn ? hn.el.getHeight() : 0);
32449         }
32450         this.adjustments = [
32451             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32452             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32453         ];
32454     }
32455
32456     if(this.draggable){
32457         this.dd = this.dynamic ?
32458             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32459         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32460     }
32461
32462     // public events
32463     this.addEvents({
32464         /**
32465          * @event beforeresize
32466          * Fired before resize is allowed. Set enabled to false to cancel resize.
32467          * @param {Roo.Resizable} this
32468          * @param {Roo.EventObject} e The mousedown event
32469          */
32470         "beforeresize" : true,
32471         /**
32472          * @event resizing
32473          * Fired a resizing.
32474          * @param {Roo.Resizable} this
32475          * @param {Number} x The new x position
32476          * @param {Number} y The new y position
32477          * @param {Number} w The new w width
32478          * @param {Number} h The new h hight
32479          * @param {Roo.EventObject} e The mouseup event
32480          */
32481         "resizing" : true,
32482         /**
32483          * @event resize
32484          * Fired after a resize.
32485          * @param {Roo.Resizable} this
32486          * @param {Number} width The new width
32487          * @param {Number} height The new height
32488          * @param {Roo.EventObject} e The mouseup event
32489          */
32490         "resize" : true
32491     });
32492
32493     if(this.width !== null && this.height !== null){
32494         this.resizeTo(this.width, this.height);
32495     }else{
32496         this.updateChildSize();
32497     }
32498     if(Roo.isIE){
32499         this.el.dom.style.zoom = 1;
32500     }
32501     Roo.Resizable.superclass.constructor.call(this);
32502 };
32503
32504 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32505         resizeChild : false,
32506         adjustments : [0, 0],
32507         minWidth : 5,
32508         minHeight : 5,
32509         maxWidth : 10000,
32510         maxHeight : 10000,
32511         enabled : true,
32512         animate : false,
32513         duration : .35,
32514         dynamic : false,
32515         handles : false,
32516         multiDirectional : false,
32517         disableTrackOver : false,
32518         easing : 'easeOutStrong',
32519         widthIncrement : 0,
32520         heightIncrement : 0,
32521         pinned : false,
32522         width : null,
32523         height : null,
32524         preserveRatio : false,
32525         transparent: false,
32526         minX: 0,
32527         minY: 0,
32528         draggable: false,
32529
32530         /**
32531          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32532          */
32533         constrainTo: undefined,
32534         /**
32535          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32536          */
32537         resizeRegion: undefined,
32538
32539
32540     /**
32541      * Perform a manual resize
32542      * @param {Number} width
32543      * @param {Number} height
32544      */
32545     resizeTo : function(width, height){
32546         this.el.setSize(width, height);
32547         this.updateChildSize();
32548         this.fireEvent("resize", this, width, height, null);
32549     },
32550
32551     // private
32552     startSizing : function(e, handle){
32553         this.fireEvent("beforeresize", this, e);
32554         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32555
32556             if(!this.overlay){
32557                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32558                 this.overlay.unselectable();
32559                 this.overlay.enableDisplayMode("block");
32560                 this.overlay.on("mousemove", this.onMouseMove, this);
32561                 this.overlay.on("mouseup", this.onMouseUp, this);
32562             }
32563             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32564
32565             this.resizing = true;
32566             this.startBox = this.el.getBox();
32567             this.startPoint = e.getXY();
32568             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32569                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32570
32571             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32572             this.overlay.show();
32573
32574             if(this.constrainTo) {
32575                 var ct = Roo.get(this.constrainTo);
32576                 this.resizeRegion = ct.getRegion().adjust(
32577                     ct.getFrameWidth('t'),
32578                     ct.getFrameWidth('l'),
32579                     -ct.getFrameWidth('b'),
32580                     -ct.getFrameWidth('r')
32581                 );
32582             }
32583
32584             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32585             this.proxy.show();
32586             this.proxy.setBox(this.startBox);
32587             if(!this.dynamic){
32588                 this.proxy.setStyle('visibility', 'visible');
32589             }
32590         }
32591     },
32592
32593     // private
32594     onMouseDown : function(handle, e){
32595         if(this.enabled){
32596             e.stopEvent();
32597             this.activeHandle = handle;
32598             this.startSizing(e, handle);
32599         }
32600     },
32601
32602     // private
32603     onMouseUp : function(e){
32604         var size = this.resizeElement();
32605         this.resizing = false;
32606         this.handleOut();
32607         this.overlay.hide();
32608         this.proxy.hide();
32609         this.fireEvent("resize", this, size.width, size.height, e);
32610     },
32611
32612     // private
32613     updateChildSize : function(){
32614         
32615         if(this.resizeChild){
32616             var el = this.el;
32617             var child = this.resizeChild;
32618             var adj = this.adjustments;
32619             if(el.dom.offsetWidth){
32620                 var b = el.getSize(true);
32621                 child.setSize(b.width+adj[0], b.height+adj[1]);
32622             }
32623             // Second call here for IE
32624             // The first call enables instant resizing and
32625             // the second call corrects scroll bars if they
32626             // exist
32627             if(Roo.isIE){
32628                 setTimeout(function(){
32629                     if(el.dom.offsetWidth){
32630                         var b = el.getSize(true);
32631                         child.setSize(b.width+adj[0], b.height+adj[1]);
32632                     }
32633                 }, 10);
32634             }
32635         }
32636     },
32637
32638     // private
32639     snap : function(value, inc, min){
32640         if(!inc || !value) {
32641             return value;
32642         }
32643         var newValue = value;
32644         var m = value % inc;
32645         if(m > 0){
32646             if(m > (inc/2)){
32647                 newValue = value + (inc-m);
32648             }else{
32649                 newValue = value - m;
32650             }
32651         }
32652         return Math.max(min, newValue);
32653     },
32654
32655     // private
32656     resizeElement : function(){
32657         var box = this.proxy.getBox();
32658         if(this.updateBox){
32659             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32660         }else{
32661             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32662         }
32663         this.updateChildSize();
32664         if(!this.dynamic){
32665             this.proxy.hide();
32666         }
32667         return box;
32668     },
32669
32670     // private
32671     constrain : function(v, diff, m, mx){
32672         if(v - diff < m){
32673             diff = v - m;
32674         }else if(v - diff > mx){
32675             diff = mx - v;
32676         }
32677         return diff;
32678     },
32679
32680     // private
32681     onMouseMove : function(e){
32682         
32683         if(this.enabled){
32684             try{// try catch so if something goes wrong the user doesn't get hung
32685
32686             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32687                 return;
32688             }
32689
32690             //var curXY = this.startPoint;
32691             var curSize = this.curSize || this.startBox;
32692             var x = this.startBox.x, y = this.startBox.y;
32693             var ox = x, oy = y;
32694             var w = curSize.width, h = curSize.height;
32695             var ow = w, oh = h;
32696             var mw = this.minWidth, mh = this.minHeight;
32697             var mxw = this.maxWidth, mxh = this.maxHeight;
32698             var wi = this.widthIncrement;
32699             var hi = this.heightIncrement;
32700
32701             var eventXY = e.getXY();
32702             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32703             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32704
32705             var pos = this.activeHandle.position;
32706
32707             switch(pos){
32708                 case "east":
32709                     w += diffX;
32710                     w = Math.min(Math.max(mw, w), mxw);
32711                     break;
32712              
32713                 case "south":
32714                     h += diffY;
32715                     h = Math.min(Math.max(mh, h), mxh);
32716                     break;
32717                 case "southeast":
32718                     w += diffX;
32719                     h += diffY;
32720                     w = Math.min(Math.max(mw, w), mxw);
32721                     h = Math.min(Math.max(mh, h), mxh);
32722                     break;
32723                 case "north":
32724                     diffY = this.constrain(h, diffY, mh, mxh);
32725                     y += diffY;
32726                     h -= diffY;
32727                     break;
32728                 case "hdrag":
32729                     
32730                     if (wi) {
32731                         var adiffX = Math.abs(diffX);
32732                         var sub = (adiffX % wi); // how much 
32733                         if (sub > (wi/2)) { // far enough to snap
32734                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32735                         } else {
32736                             // remove difference.. 
32737                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32738                         }
32739                     }
32740                     x += diffX;
32741                     x = Math.max(this.minX, x);
32742                     break;
32743                 case "west":
32744                     diffX = this.constrain(w, diffX, mw, mxw);
32745                     x += diffX;
32746                     w -= diffX;
32747                     break;
32748                 case "northeast":
32749                     w += diffX;
32750                     w = Math.min(Math.max(mw, w), mxw);
32751                     diffY = this.constrain(h, diffY, mh, mxh);
32752                     y += diffY;
32753                     h -= diffY;
32754                     break;
32755                 case "northwest":
32756                     diffX = this.constrain(w, diffX, mw, mxw);
32757                     diffY = this.constrain(h, diffY, mh, mxh);
32758                     y += diffY;
32759                     h -= diffY;
32760                     x += diffX;
32761                     w -= diffX;
32762                     break;
32763                case "southwest":
32764                     diffX = this.constrain(w, diffX, mw, mxw);
32765                     h += diffY;
32766                     h = Math.min(Math.max(mh, h), mxh);
32767                     x += diffX;
32768                     w -= diffX;
32769                     break;
32770             }
32771
32772             var sw = this.snap(w, wi, mw);
32773             var sh = this.snap(h, hi, mh);
32774             if(sw != w || sh != h){
32775                 switch(pos){
32776                     case "northeast":
32777                         y -= sh - h;
32778                     break;
32779                     case "north":
32780                         y -= sh - h;
32781                         break;
32782                     case "southwest":
32783                         x -= sw - w;
32784                     break;
32785                     case "west":
32786                         x -= sw - w;
32787                         break;
32788                     case "northwest":
32789                         x -= sw - w;
32790                         y -= sh - h;
32791                     break;
32792                 }
32793                 w = sw;
32794                 h = sh;
32795             }
32796
32797             if(this.preserveRatio){
32798                 switch(pos){
32799                     case "southeast":
32800                     case "east":
32801                         h = oh * (w/ow);
32802                         h = Math.min(Math.max(mh, h), mxh);
32803                         w = ow * (h/oh);
32804                        break;
32805                     case "south":
32806                         w = ow * (h/oh);
32807                         w = Math.min(Math.max(mw, w), mxw);
32808                         h = oh * (w/ow);
32809                         break;
32810                     case "northeast":
32811                         w = ow * (h/oh);
32812                         w = Math.min(Math.max(mw, w), mxw);
32813                         h = oh * (w/ow);
32814                     break;
32815                     case "north":
32816                         var tw = w;
32817                         w = ow * (h/oh);
32818                         w = Math.min(Math.max(mw, w), mxw);
32819                         h = oh * (w/ow);
32820                         x += (tw - w) / 2;
32821                         break;
32822                     case "southwest":
32823                         h = oh * (w/ow);
32824                         h = Math.min(Math.max(mh, h), mxh);
32825                         var tw = w;
32826                         w = ow * (h/oh);
32827                         x += tw - w;
32828                         break;
32829                     case "west":
32830                         var th = h;
32831                         h = oh * (w/ow);
32832                         h = Math.min(Math.max(mh, h), mxh);
32833                         y += (th - h) / 2;
32834                         var tw = w;
32835                         w = ow * (h/oh);
32836                         x += tw - w;
32837                        break;
32838                     case "northwest":
32839                         var tw = w;
32840                         var th = h;
32841                         h = oh * (w/ow);
32842                         h = Math.min(Math.max(mh, h), mxh);
32843                         w = ow * (h/oh);
32844                         y += th - h;
32845                         x += tw - w;
32846                        break;
32847
32848                 }
32849             }
32850             if (pos == 'hdrag') {
32851                 w = ow;
32852             }
32853             this.proxy.setBounds(x, y, w, h);
32854             if(this.dynamic){
32855                 this.resizeElement();
32856             }
32857             }catch(e){}
32858         }
32859         this.fireEvent("resizing", this, x, y, w, h, e);
32860     },
32861
32862     // private
32863     handleOver : function(){
32864         if(this.enabled){
32865             this.el.addClass("x-resizable-over");
32866         }
32867     },
32868
32869     // private
32870     handleOut : function(){
32871         if(!this.resizing){
32872             this.el.removeClass("x-resizable-over");
32873         }
32874     },
32875
32876     /**
32877      * Returns the element this component is bound to.
32878      * @return {Roo.Element}
32879      */
32880     getEl : function(){
32881         return this.el;
32882     },
32883
32884     /**
32885      * Returns the resizeChild element (or null).
32886      * @return {Roo.Element}
32887      */
32888     getResizeChild : function(){
32889         return this.resizeChild;
32890     },
32891     groupHandler : function()
32892     {
32893         
32894     },
32895     /**
32896      * Destroys this resizable. If the element was wrapped and
32897      * removeEl is not true then the element remains.
32898      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32899      */
32900     destroy : function(removeEl){
32901         this.proxy.remove();
32902         if(this.overlay){
32903             this.overlay.removeAllListeners();
32904             this.overlay.remove();
32905         }
32906         var ps = Roo.Resizable.positions;
32907         for(var k in ps){
32908             if(typeof ps[k] != "function" && this[ps[k]]){
32909                 var h = this[ps[k]];
32910                 h.el.removeAllListeners();
32911                 h.el.remove();
32912             }
32913         }
32914         if(removeEl){
32915             this.el.update("");
32916             this.el.remove();
32917         }
32918     }
32919 });
32920
32921 // private
32922 // hash to map config positions to true positions
32923 Roo.Resizable.positions = {
32924     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32925     hd: "hdrag"
32926 };
32927
32928 // private
32929 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32930     if(!this.tpl){
32931         // only initialize the template if resizable is used
32932         var tpl = Roo.DomHelper.createTemplate(
32933             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32934         );
32935         tpl.compile();
32936         Roo.Resizable.Handle.prototype.tpl = tpl;
32937     }
32938     this.position = pos;
32939     this.rz = rz;
32940     // show north drag fro topdra
32941     var handlepos = pos == 'hdrag' ? 'north' : pos;
32942     
32943     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32944     if (pos == 'hdrag') {
32945         this.el.setStyle('cursor', 'pointer');
32946     }
32947     this.el.unselectable();
32948     if(transparent){
32949         this.el.setOpacity(0);
32950     }
32951     this.el.on("mousedown", this.onMouseDown, this);
32952     if(!disableTrackOver){
32953         this.el.on("mouseover", this.onMouseOver, this);
32954         this.el.on("mouseout", this.onMouseOut, this);
32955     }
32956 };
32957
32958 // private
32959 Roo.Resizable.Handle.prototype = {
32960     afterResize : function(rz){
32961         Roo.log('after?');
32962         // do nothing
32963     },
32964     // private
32965     onMouseDown : function(e){
32966         this.rz.onMouseDown(this, e);
32967     },
32968     // private
32969     onMouseOver : function(e){
32970         this.rz.handleOver(this, e);
32971     },
32972     // private
32973     onMouseOut : function(e){
32974         this.rz.handleOut(this, e);
32975     }
32976 };/*
32977  * Based on:
32978  * Ext JS Library 1.1.1
32979  * Copyright(c) 2006-2007, Ext JS, LLC.
32980  *
32981  * Originally Released Under LGPL - original licence link has changed is not relivant.
32982  *
32983  * Fork - LGPL
32984  * <script type="text/javascript">
32985  */
32986
32987 /**
32988  * @class Roo.Editor
32989  * @extends Roo.Component
32990  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32991  * @constructor
32992  * Create a new Editor
32993  * @param {Roo.form.Field} field The Field object (or descendant)
32994  * @param {Object} config The config object
32995  */
32996 Roo.Editor = function(field, config){
32997     Roo.Editor.superclass.constructor.call(this, config);
32998     this.field = field;
32999     this.addEvents({
33000         /**
33001              * @event beforestartedit
33002              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33003              * false from the handler of this event.
33004              * @param {Editor} this
33005              * @param {Roo.Element} boundEl The underlying element bound to this editor
33006              * @param {Mixed} value The field value being set
33007              */
33008         "beforestartedit" : true,
33009         /**
33010              * @event startedit
33011              * Fires when this editor is displayed
33012              * @param {Roo.Element} boundEl The underlying element bound to this editor
33013              * @param {Mixed} value The starting field value
33014              */
33015         "startedit" : true,
33016         /**
33017              * @event beforecomplete
33018              * Fires after a change has been made to the field, but before the change is reflected in the underlying
33019              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
33020              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
33021              * event will not fire since no edit actually occurred.
33022              * @param {Editor} this
33023              * @param {Mixed} value The current field value
33024              * @param {Mixed} startValue The original field value
33025              */
33026         "beforecomplete" : true,
33027         /**
33028              * @event complete
33029              * Fires after editing is complete and any changed value has been written to the underlying field.
33030              * @param {Editor} this
33031              * @param {Mixed} value The current field value
33032              * @param {Mixed} startValue The original field value
33033              */
33034         "complete" : true,
33035         /**
33036          * @event specialkey
33037          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
33038          * {@link Roo.EventObject#getKey} to determine which key was pressed.
33039          * @param {Roo.form.Field} this
33040          * @param {Roo.EventObject} e The event object
33041          */
33042         "specialkey" : true
33043     });
33044 };
33045
33046 Roo.extend(Roo.Editor, Roo.Component, {
33047     /**
33048      * @cfg {Boolean/String} autosize
33049      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
33050      * or "height" to adopt the height only (defaults to false)
33051      */
33052     /**
33053      * @cfg {Boolean} revertInvalid
33054      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
33055      * validation fails (defaults to true)
33056      */
33057     /**
33058      * @cfg {Boolean} ignoreNoChange
33059      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
33060      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
33061      * will never be ignored.
33062      */
33063     /**
33064      * @cfg {Boolean} hideEl
33065      * False to keep the bound element visible while the editor is displayed (defaults to true)
33066      */
33067     /**
33068      * @cfg {Mixed} value
33069      * The data value of the underlying field (defaults to "")
33070      */
33071     value : "",
33072     /**
33073      * @cfg {String} alignment
33074      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
33075      */
33076     alignment: "c-c?",
33077     /**
33078      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
33079      * for bottom-right shadow (defaults to "frame")
33080      */
33081     shadow : "frame",
33082     /**
33083      * @cfg {Boolean} constrain True to constrain the editor to the viewport
33084      */
33085     constrain : false,
33086     /**
33087      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
33088      */
33089     completeOnEnter : false,
33090     /**
33091      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
33092      */
33093     cancelOnEsc : false,
33094     /**
33095      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
33096      */
33097     updateEl : false,
33098
33099     // private
33100     onRender : function(ct, position){
33101         this.el = new Roo.Layer({
33102             shadow: this.shadow,
33103             cls: "x-editor",
33104             parentEl : ct,
33105             shim : this.shim,
33106             shadowOffset:4,
33107             id: this.id,
33108             constrain: this.constrain
33109         });
33110         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
33111         if(this.field.msgTarget != 'title'){
33112             this.field.msgTarget = 'qtip';
33113         }
33114         this.field.render(this.el);
33115         if(Roo.isGecko){
33116             this.field.el.dom.setAttribute('autocomplete', 'off');
33117         }
33118         this.field.on("specialkey", this.onSpecialKey, this);
33119         if(this.swallowKeys){
33120             this.field.el.swallowEvent(['keydown','keypress']);
33121         }
33122         this.field.show();
33123         this.field.on("blur", this.onBlur, this);
33124         if(this.field.grow){
33125             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
33126         }
33127     },
33128
33129     onSpecialKey : function(field, e)
33130     {
33131         //Roo.log('editor onSpecialKey');
33132         if(this.completeOnEnter && e.getKey() == e.ENTER){
33133             e.stopEvent();
33134             this.completeEdit();
33135             return;
33136         }
33137         // do not fire special key otherwise it might hide close the editor...
33138         if(e.getKey() == e.ENTER){    
33139             return;
33140         }
33141         if(this.cancelOnEsc && e.getKey() == e.ESC){
33142             this.cancelEdit();
33143             return;
33144         } 
33145         this.fireEvent('specialkey', field, e);
33146     
33147     },
33148
33149     /**
33150      * Starts the editing process and shows the editor.
33151      * @param {String/HTMLElement/Element} el The element to edit
33152      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
33153       * to the innerHTML of el.
33154      */
33155     startEdit : function(el, value){
33156         if(this.editing){
33157             this.completeEdit();
33158         }
33159         this.boundEl = Roo.get(el);
33160         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
33161         if(!this.rendered){
33162             this.render(this.parentEl || document.body);
33163         }
33164         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
33165             return;
33166         }
33167         this.startValue = v;
33168         this.field.setValue(v);
33169         if(this.autoSize){
33170             var sz = this.boundEl.getSize();
33171             switch(this.autoSize){
33172                 case "width":
33173                 this.setSize(sz.width,  "");
33174                 break;
33175                 case "height":
33176                 this.setSize("",  sz.height);
33177                 break;
33178                 default:
33179                 this.setSize(sz.width,  sz.height);
33180             }
33181         }
33182         this.el.alignTo(this.boundEl, this.alignment);
33183         this.editing = true;
33184         if(Roo.QuickTips){
33185             Roo.QuickTips.disable();
33186         }
33187         this.show();
33188     },
33189
33190     /**
33191      * Sets the height and width of this editor.
33192      * @param {Number} width The new width
33193      * @param {Number} height The new height
33194      */
33195     setSize : function(w, h){
33196         this.field.setSize(w, h);
33197         if(this.el){
33198             this.el.sync();
33199         }
33200     },
33201
33202     /**
33203      * Realigns the editor to the bound field based on the current alignment config value.
33204      */
33205     realign : function(){
33206         this.el.alignTo(this.boundEl, this.alignment);
33207     },
33208
33209     /**
33210      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
33211      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
33212      */
33213     completeEdit : function(remainVisible){
33214         if(!this.editing){
33215             return;
33216         }
33217         var v = this.getValue();
33218         if(this.revertInvalid !== false && !this.field.isValid()){
33219             v = this.startValue;
33220             this.cancelEdit(true);
33221         }
33222         if(String(v) === String(this.startValue) && this.ignoreNoChange){
33223             this.editing = false;
33224             this.hide();
33225             return;
33226         }
33227         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
33228             this.editing = false;
33229             if(this.updateEl && this.boundEl){
33230                 this.boundEl.update(v);
33231             }
33232             if(remainVisible !== true){
33233                 this.hide();
33234             }
33235             this.fireEvent("complete", this, v, this.startValue);
33236         }
33237     },
33238
33239     // private
33240     onShow : function(){
33241         this.el.show();
33242         if(this.hideEl !== false){
33243             this.boundEl.hide();
33244         }
33245         this.field.show();
33246         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
33247             this.fixIEFocus = true;
33248             this.deferredFocus.defer(50, this);
33249         }else{
33250             this.field.focus();
33251         }
33252         this.fireEvent("startedit", this.boundEl, this.startValue);
33253     },
33254
33255     deferredFocus : function(){
33256         if(this.editing){
33257             this.field.focus();
33258         }
33259     },
33260
33261     /**
33262      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
33263      * reverted to the original starting value.
33264      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
33265      * cancel (defaults to false)
33266      */
33267     cancelEdit : function(remainVisible){
33268         if(this.editing){
33269             this.setValue(this.startValue);
33270             if(remainVisible !== true){
33271                 this.hide();
33272             }
33273         }
33274     },
33275
33276     // private
33277     onBlur : function(){
33278         if(this.allowBlur !== true && this.editing){
33279             this.completeEdit();
33280         }
33281     },
33282
33283     // private
33284     onHide : function(){
33285         if(this.editing){
33286             this.completeEdit();
33287             return;
33288         }
33289         this.field.blur();
33290         if(this.field.collapse){
33291             this.field.collapse();
33292         }
33293         this.el.hide();
33294         if(this.hideEl !== false){
33295             this.boundEl.show();
33296         }
33297         if(Roo.QuickTips){
33298             Roo.QuickTips.enable();
33299         }
33300     },
33301
33302     /**
33303      * Sets the data value of the editor
33304      * @param {Mixed} value Any valid value supported by the underlying field
33305      */
33306     setValue : function(v){
33307         this.field.setValue(v);
33308     },
33309
33310     /**
33311      * Gets the data value of the editor
33312      * @return {Mixed} The data value
33313      */
33314     getValue : function(){
33315         return this.field.getValue();
33316     }
33317 });/*
33318  * Based on:
33319  * Ext JS Library 1.1.1
33320  * Copyright(c) 2006-2007, Ext JS, LLC.
33321  *
33322  * Originally Released Under LGPL - original licence link has changed is not relivant.
33323  *
33324  * Fork - LGPL
33325  * <script type="text/javascript">
33326  */
33327  
33328 /**
33329  * @class Roo.BasicDialog
33330  * @extends Roo.util.Observable
33331  * @parent none builder
33332  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
33333  * <pre><code>
33334 var dlg = new Roo.BasicDialog("my-dlg", {
33335     height: 200,
33336     width: 300,
33337     minHeight: 100,
33338     minWidth: 150,
33339     modal: true,
33340     proxyDrag: true,
33341     shadow: true
33342 });
33343 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33344 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
33345 dlg.addButton('Cancel', dlg.hide, dlg);
33346 dlg.show();
33347 </code></pre>
33348   <b>A Dialog should always be a direct child of the body element.</b>
33349  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33350  * @cfg {String} title Default text to display in the title bar (defaults to null)
33351  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33352  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33353  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33354  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33355  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33356  * (defaults to null with no animation)
33357  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33358  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33359  * property for valid values (defaults to 'all')
33360  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33361  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33362  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33363  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33364  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33365  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33366  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33367  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33368  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33369  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33370  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33371  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33372  * draggable = true (defaults to false)
33373  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33374  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33375  * shadow (defaults to false)
33376  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33377  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33378  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33379  * @cfg {Array} buttons Array of buttons
33380  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33381  * @constructor
33382  * Create a new BasicDialog.
33383  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33384  * @param {Object} config Configuration options
33385  */
33386 Roo.BasicDialog = function(el, config){
33387     this.el = Roo.get(el);
33388     var dh = Roo.DomHelper;
33389     if(!this.el && config && config.autoCreate){
33390         if(typeof config.autoCreate == "object"){
33391             if(!config.autoCreate.id){
33392                 config.autoCreate.id = el;
33393             }
33394             this.el = dh.append(document.body,
33395                         config.autoCreate, true);
33396         }else{
33397             this.el = dh.append(document.body,
33398                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33399         }
33400     }
33401     el = this.el;
33402     el.setDisplayed(true);
33403     el.hide = this.hideAction;
33404     this.id = el.id;
33405     el.addClass("x-dlg");
33406
33407     Roo.apply(this, config);
33408
33409     this.proxy = el.createProxy("x-dlg-proxy");
33410     this.proxy.hide = this.hideAction;
33411     this.proxy.setOpacity(.5);
33412     this.proxy.hide();
33413
33414     if(config.width){
33415         el.setWidth(config.width);
33416     }
33417     if(config.height){
33418         el.setHeight(config.height);
33419     }
33420     this.size = el.getSize();
33421     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33422         this.xy = [config.x,config.y];
33423     }else{
33424         this.xy = el.getCenterXY(true);
33425     }
33426     /** The header element @type Roo.Element */
33427     this.header = el.child("> .x-dlg-hd");
33428     /** The body element @type Roo.Element */
33429     this.body = el.child("> .x-dlg-bd");
33430     /** The footer element @type Roo.Element */
33431     this.footer = el.child("> .x-dlg-ft");
33432
33433     if(!this.header){
33434         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33435     }
33436     if(!this.body){
33437         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33438     }
33439
33440     this.header.unselectable();
33441     if(this.title){
33442         this.header.update(this.title);
33443     }
33444     // this element allows the dialog to be focused for keyboard event
33445     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33446     this.focusEl.swallowEvent("click", true);
33447
33448     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33449
33450     // wrap the body and footer for special rendering
33451     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33452     if(this.footer){
33453         this.bwrap.dom.appendChild(this.footer.dom);
33454     }
33455
33456     this.bg = this.el.createChild({
33457         tag: "div", cls:"x-dlg-bg",
33458         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33459     });
33460     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33461
33462
33463     if(this.autoScroll !== false && !this.autoTabs){
33464         this.body.setStyle("overflow", "auto");
33465     }
33466
33467     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33468
33469     if(this.closable !== false){
33470         this.el.addClass("x-dlg-closable");
33471         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33472         this.close.on("click", this.closeClick, this);
33473         this.close.addClassOnOver("x-dlg-close-over");
33474     }
33475     if(this.collapsible !== false){
33476         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33477         this.collapseBtn.on("click", this.collapseClick, this);
33478         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33479         this.header.on("dblclick", this.collapseClick, this);
33480     }
33481     if(this.resizable !== false){
33482         this.el.addClass("x-dlg-resizable");
33483         this.resizer = new Roo.Resizable(el, {
33484             minWidth: this.minWidth || 80,
33485             minHeight:this.minHeight || 80,
33486             handles: this.resizeHandles || "all",
33487             pinned: true
33488         });
33489         this.resizer.on("beforeresize", this.beforeResize, this);
33490         this.resizer.on("resize", this.onResize, this);
33491     }
33492     if(this.draggable !== false){
33493         el.addClass("x-dlg-draggable");
33494         if (!this.proxyDrag) {
33495             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33496         }
33497         else {
33498             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33499         }
33500         dd.setHandleElId(this.header.id);
33501         dd.endDrag = this.endMove.createDelegate(this);
33502         dd.startDrag = this.startMove.createDelegate(this);
33503         dd.onDrag = this.onDrag.createDelegate(this);
33504         dd.scroll = false;
33505         this.dd = dd;
33506     }
33507     if(this.modal){
33508         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33509         this.mask.enableDisplayMode("block");
33510         this.mask.hide();
33511         this.el.addClass("x-dlg-modal");
33512     }
33513     if(this.shadow){
33514         this.shadow = new Roo.Shadow({
33515             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33516             offset : this.shadowOffset
33517         });
33518     }else{
33519         this.shadowOffset = 0;
33520     }
33521     if(Roo.useShims && this.shim !== false){
33522         this.shim = this.el.createShim();
33523         this.shim.hide = this.hideAction;
33524         this.shim.hide();
33525     }else{
33526         this.shim = false;
33527     }
33528     if(this.autoTabs){
33529         this.initTabs();
33530     }
33531     if (this.buttons) { 
33532         var bts= this.buttons;
33533         this.buttons = [];
33534         Roo.each(bts, function(b) {
33535             this.addButton(b);
33536         }, this);
33537     }
33538     
33539     
33540     this.addEvents({
33541         /**
33542          * @event keydown
33543          * Fires when a key is pressed
33544          * @param {Roo.BasicDialog} this
33545          * @param {Roo.EventObject} e
33546          */
33547         "keydown" : true,
33548         /**
33549          * @event move
33550          * Fires when this dialog is moved by the user.
33551          * @param {Roo.BasicDialog} this
33552          * @param {Number} x The new page X
33553          * @param {Number} y The new page Y
33554          */
33555         "move" : true,
33556         /**
33557          * @event resize
33558          * Fires when this dialog is resized by the user.
33559          * @param {Roo.BasicDialog} this
33560          * @param {Number} width The new width
33561          * @param {Number} height The new height
33562          */
33563         "resize" : true,
33564         /**
33565          * @event beforehide
33566          * Fires before this dialog is hidden.
33567          * @param {Roo.BasicDialog} this
33568          */
33569         "beforehide" : true,
33570         /**
33571          * @event hide
33572          * Fires when this dialog is hidden.
33573          * @param {Roo.BasicDialog} this
33574          */
33575         "hide" : true,
33576         /**
33577          * @event beforeshow
33578          * Fires before this dialog is shown.
33579          * @param {Roo.BasicDialog} this
33580          */
33581         "beforeshow" : true,
33582         /**
33583          * @event show
33584          * Fires when this dialog is shown.
33585          * @param {Roo.BasicDialog} this
33586          */
33587         "show" : true
33588     });
33589     el.on("keydown", this.onKeyDown, this);
33590     el.on("mousedown", this.toFront, this);
33591     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33592     this.el.hide();
33593     Roo.DialogManager.register(this);
33594     Roo.BasicDialog.superclass.constructor.call(this);
33595 };
33596
33597 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33598     shadowOffset: Roo.isIE ? 6 : 5,
33599     minHeight: 80,
33600     minWidth: 200,
33601     minButtonWidth: 75,
33602     defaultButton: null,
33603     buttonAlign: "right",
33604     tabTag: 'div',
33605     firstShow: true,
33606
33607     /**
33608      * Sets the dialog title text
33609      * @param {String} text The title text to display
33610      * @return {Roo.BasicDialog} this
33611      */
33612     setTitle : function(text){
33613         this.header.update(text);
33614         return this;
33615     },
33616
33617     // private
33618     closeClick : function(){
33619         this.hide();
33620     },
33621
33622     // private
33623     collapseClick : function(){
33624         this[this.collapsed ? "expand" : "collapse"]();
33625     },
33626
33627     /**
33628      * Collapses the dialog to its minimized state (only the title bar is visible).
33629      * Equivalent to the user clicking the collapse dialog button.
33630      */
33631     collapse : function(){
33632         if(!this.collapsed){
33633             this.collapsed = true;
33634             this.el.addClass("x-dlg-collapsed");
33635             this.restoreHeight = this.el.getHeight();
33636             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33637         }
33638     },
33639
33640     /**
33641      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33642      * clicking the expand dialog button.
33643      */
33644     expand : function(){
33645         if(this.collapsed){
33646             this.collapsed = false;
33647             this.el.removeClass("x-dlg-collapsed");
33648             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33649         }
33650     },
33651
33652     /**
33653      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33654      * @return {Roo.TabPanel} The tabs component
33655      */
33656     initTabs : function(){
33657         var tabs = this.getTabs();
33658         while(tabs.getTab(0)){
33659             tabs.removeTab(0);
33660         }
33661         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33662             var dom = el.dom;
33663             tabs.addTab(Roo.id(dom), dom.title);
33664             dom.title = "";
33665         });
33666         tabs.activate(0);
33667         return tabs;
33668     },
33669
33670     // private
33671     beforeResize : function(){
33672         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33673     },
33674
33675     // private
33676     onResize : function(){
33677         this.refreshSize();
33678         this.syncBodyHeight();
33679         this.adjustAssets();
33680         this.focus();
33681         this.fireEvent("resize", this, this.size.width, this.size.height);
33682     },
33683
33684     // private
33685     onKeyDown : function(e){
33686         if(this.isVisible()){
33687             this.fireEvent("keydown", this, e);
33688         }
33689     },
33690
33691     /**
33692      * Resizes the dialog.
33693      * @param {Number} width
33694      * @param {Number} height
33695      * @return {Roo.BasicDialog} this
33696      */
33697     resizeTo : function(width, height){
33698         this.el.setSize(width, height);
33699         this.size = {width: width, height: height};
33700         this.syncBodyHeight();
33701         if(this.fixedcenter){
33702             this.center();
33703         }
33704         if(this.isVisible()){
33705             this.constrainXY();
33706             this.adjustAssets();
33707         }
33708         this.fireEvent("resize", this, width, height);
33709         return this;
33710     },
33711
33712
33713     /**
33714      * Resizes the dialog to fit the specified content size.
33715      * @param {Number} width
33716      * @param {Number} height
33717      * @return {Roo.BasicDialog} this
33718      */
33719     setContentSize : function(w, h){
33720         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33721         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33722         //if(!this.el.isBorderBox()){
33723             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33724             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33725         //}
33726         if(this.tabs){
33727             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33728             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33729         }
33730         this.resizeTo(w, h);
33731         return this;
33732     },
33733
33734     /**
33735      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33736      * executed in response to a particular key being pressed while the dialog is active.
33737      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33738      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33739      * @param {Function} fn The function to call
33740      * @param {Object} scope (optional) The scope of the function
33741      * @return {Roo.BasicDialog} this
33742      */
33743     addKeyListener : function(key, fn, scope){
33744         var keyCode, shift, ctrl, alt;
33745         if(typeof key == "object" && !(key instanceof Array)){
33746             keyCode = key["key"];
33747             shift = key["shift"];
33748             ctrl = key["ctrl"];
33749             alt = key["alt"];
33750         }else{
33751             keyCode = key;
33752         }
33753         var handler = function(dlg, e){
33754             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33755                 var k = e.getKey();
33756                 if(keyCode instanceof Array){
33757                     for(var i = 0, len = keyCode.length; i < len; i++){
33758                         if(keyCode[i] == k){
33759                           fn.call(scope || window, dlg, k, e);
33760                           return;
33761                         }
33762                     }
33763                 }else{
33764                     if(k == keyCode){
33765                         fn.call(scope || window, dlg, k, e);
33766                     }
33767                 }
33768             }
33769         };
33770         this.on("keydown", handler);
33771         return this;
33772     },
33773
33774     /**
33775      * Returns the TabPanel component (creates it if it doesn't exist).
33776      * Note: If you wish to simply check for the existence of tabs without creating them,
33777      * check for a null 'tabs' property.
33778      * @return {Roo.TabPanel} The tabs component
33779      */
33780     getTabs : function(){
33781         if(!this.tabs){
33782             this.el.addClass("x-dlg-auto-tabs");
33783             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33784             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33785         }
33786         return this.tabs;
33787     },
33788
33789     /**
33790      * Adds a button to the footer section of the dialog.
33791      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33792      * object or a valid Roo.DomHelper element config
33793      * @param {Function} handler The function called when the button is clicked
33794      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33795      * @return {Roo.Button} The new button
33796      */
33797     addButton : function(config, handler, scope){
33798         var dh = Roo.DomHelper;
33799         if(!this.footer){
33800             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33801         }
33802         if(!this.btnContainer){
33803             var tb = this.footer.createChild({
33804
33805                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33806                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33807             }, null, true);
33808             this.btnContainer = tb.firstChild.firstChild.firstChild;
33809         }
33810         var bconfig = {
33811             handler: handler,
33812             scope: scope,
33813             minWidth: this.minButtonWidth,
33814             hideParent:true
33815         };
33816         if(typeof config == "string"){
33817             bconfig.text = config;
33818         }else{
33819             if(config.tag){
33820                 bconfig.dhconfig = config;
33821             }else{
33822                 Roo.apply(bconfig, config);
33823             }
33824         }
33825         var fc = false;
33826         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33827             bconfig.position = Math.max(0, bconfig.position);
33828             fc = this.btnContainer.childNodes[bconfig.position];
33829         }
33830          
33831         var btn = new Roo.Button(
33832             fc ? 
33833                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33834                 : this.btnContainer.appendChild(document.createElement("td")),
33835             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33836             bconfig
33837         );
33838         this.syncBodyHeight();
33839         if(!this.buttons){
33840             /**
33841              * Array of all the buttons that have been added to this dialog via addButton
33842              * @type Array
33843              */
33844             this.buttons = [];
33845         }
33846         this.buttons.push(btn);
33847         return btn;
33848     },
33849
33850     /**
33851      * Sets the default button to be focused when the dialog is displayed.
33852      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33853      * @return {Roo.BasicDialog} this
33854      */
33855     setDefaultButton : function(btn){
33856         this.defaultButton = btn;
33857         return this;
33858     },
33859
33860     // private
33861     getHeaderFooterHeight : function(safe){
33862         var height = 0;
33863         if(this.header){
33864            height += this.header.getHeight();
33865         }
33866         if(this.footer){
33867            var fm = this.footer.getMargins();
33868             height += (this.footer.getHeight()+fm.top+fm.bottom);
33869         }
33870         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33871         height += this.centerBg.getPadding("tb");
33872         return height;
33873     },
33874
33875     // private
33876     syncBodyHeight : function()
33877     {
33878         var bd = this.body, // the text
33879             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33880             bw = this.bwrap;
33881         var height = this.size.height - this.getHeaderFooterHeight(false);
33882         bd.setHeight(height-bd.getMargins("tb"));
33883         var hh = this.header.getHeight();
33884         var h = this.size.height-hh;
33885         cb.setHeight(h);
33886         
33887         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33888         bw.setHeight(h-cb.getPadding("tb"));
33889         
33890         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33891         bd.setWidth(bw.getWidth(true));
33892         if(this.tabs){
33893             this.tabs.syncHeight();
33894             if(Roo.isIE){
33895                 this.tabs.el.repaint();
33896             }
33897         }
33898     },
33899
33900     /**
33901      * Restores the previous state of the dialog if Roo.state is configured.
33902      * @return {Roo.BasicDialog} this
33903      */
33904     restoreState : function(){
33905         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33906         if(box && box.width){
33907             this.xy = [box.x, box.y];
33908             this.resizeTo(box.width, box.height);
33909         }
33910         return this;
33911     },
33912
33913     // private
33914     beforeShow : function(){
33915         this.expand();
33916         if(this.fixedcenter){
33917             this.xy = this.el.getCenterXY(true);
33918         }
33919         if(this.modal){
33920             Roo.get(document.body).addClass("x-body-masked");
33921             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33922             this.mask.show();
33923         }
33924         this.constrainXY();
33925     },
33926
33927     // private
33928     animShow : function(){
33929         var b = Roo.get(this.animateTarget).getBox();
33930         this.proxy.setSize(b.width, b.height);
33931         this.proxy.setLocation(b.x, b.y);
33932         this.proxy.show();
33933         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33934                     true, .35, this.showEl.createDelegate(this));
33935     },
33936
33937     /**
33938      * Shows the dialog.
33939      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33940      * @return {Roo.BasicDialog} this
33941      */
33942     show : function(animateTarget){
33943         if (this.fireEvent("beforeshow", this) === false){
33944             return;
33945         }
33946         if(this.syncHeightBeforeShow){
33947             this.syncBodyHeight();
33948         }else if(this.firstShow){
33949             this.firstShow = false;
33950             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33951         }
33952         this.animateTarget = animateTarget || this.animateTarget;
33953         if(!this.el.isVisible()){
33954             this.beforeShow();
33955             if(this.animateTarget && Roo.get(this.animateTarget)){
33956                 this.animShow();
33957             }else{
33958                 this.showEl();
33959             }
33960         }
33961         return this;
33962     },
33963
33964     // private
33965     showEl : function(){
33966         this.proxy.hide();
33967         this.el.setXY(this.xy);
33968         this.el.show();
33969         this.adjustAssets(true);
33970         this.toFront();
33971         this.focus();
33972         // IE peekaboo bug - fix found by Dave Fenwick
33973         if(Roo.isIE){
33974             this.el.repaint();
33975         }
33976         this.fireEvent("show", this);
33977     },
33978
33979     /**
33980      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33981      * dialog itself will receive focus.
33982      */
33983     focus : function(){
33984         if(this.defaultButton){
33985             this.defaultButton.focus();
33986         }else{
33987             this.focusEl.focus();
33988         }
33989     },
33990
33991     // private
33992     constrainXY : function(){
33993         if(this.constraintoviewport !== false){
33994             if(!this.viewSize){
33995                 if(this.container){
33996                     var s = this.container.getSize();
33997                     this.viewSize = [s.width, s.height];
33998                 }else{
33999                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
34000                 }
34001             }
34002             var s = Roo.get(this.container||document).getScroll();
34003
34004             var x = this.xy[0], y = this.xy[1];
34005             var w = this.size.width, h = this.size.height;
34006             var vw = this.viewSize[0], vh = this.viewSize[1];
34007             // only move it if it needs it
34008             var moved = false;
34009             // first validate right/bottom
34010             if(x + w > vw+s.left){
34011                 x = vw - w;
34012                 moved = true;
34013             }
34014             if(y + h > vh+s.top){
34015                 y = vh - h;
34016                 moved = true;
34017             }
34018             // then make sure top/left isn't negative
34019             if(x < s.left){
34020                 x = s.left;
34021                 moved = true;
34022             }
34023             if(y < s.top){
34024                 y = s.top;
34025                 moved = true;
34026             }
34027             if(moved){
34028                 // cache xy
34029                 this.xy = [x, y];
34030                 if(this.isVisible()){
34031                     this.el.setLocation(x, y);
34032                     this.adjustAssets();
34033                 }
34034             }
34035         }
34036     },
34037
34038     // private
34039     onDrag : function(){
34040         if(!this.proxyDrag){
34041             this.xy = this.el.getXY();
34042             this.adjustAssets();
34043         }
34044     },
34045
34046     // private
34047     adjustAssets : function(doShow){
34048         var x = this.xy[0], y = this.xy[1];
34049         var w = this.size.width, h = this.size.height;
34050         if(doShow === true){
34051             if(this.shadow){
34052                 this.shadow.show(this.el);
34053             }
34054             if(this.shim){
34055                 this.shim.show();
34056             }
34057         }
34058         if(this.shadow && this.shadow.isVisible()){
34059             this.shadow.show(this.el);
34060         }
34061         if(this.shim && this.shim.isVisible()){
34062             this.shim.setBounds(x, y, w, h);
34063         }
34064     },
34065
34066     // private
34067     adjustViewport : function(w, h){
34068         if(!w || !h){
34069             w = Roo.lib.Dom.getViewWidth();
34070             h = Roo.lib.Dom.getViewHeight();
34071         }
34072         // cache the size
34073         this.viewSize = [w, h];
34074         if(this.modal && this.mask.isVisible()){
34075             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
34076             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34077         }
34078         if(this.isVisible()){
34079             this.constrainXY();
34080         }
34081     },
34082
34083     /**
34084      * Destroys this dialog and all its supporting elements (including any tabs, shim,
34085      * shadow, proxy, mask, etc.)  Also removes all event listeners.
34086      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
34087      */
34088     destroy : function(removeEl){
34089         if(this.isVisible()){
34090             this.animateTarget = null;
34091             this.hide();
34092         }
34093         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
34094         if(this.tabs){
34095             this.tabs.destroy(removeEl);
34096         }
34097         Roo.destroy(
34098              this.shim,
34099              this.proxy,
34100              this.resizer,
34101              this.close,
34102              this.mask
34103         );
34104         if(this.dd){
34105             this.dd.unreg();
34106         }
34107         if(this.buttons){
34108            for(var i = 0, len = this.buttons.length; i < len; i++){
34109                this.buttons[i].destroy();
34110            }
34111         }
34112         this.el.removeAllListeners();
34113         if(removeEl === true){
34114             this.el.update("");
34115             this.el.remove();
34116         }
34117         Roo.DialogManager.unregister(this);
34118     },
34119
34120     // private
34121     startMove : function(){
34122         if(this.proxyDrag){
34123             this.proxy.show();
34124         }
34125         if(this.constraintoviewport !== false){
34126             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
34127         }
34128     },
34129
34130     // private
34131     endMove : function(){
34132         if(!this.proxyDrag){
34133             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
34134         }else{
34135             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
34136             this.proxy.hide();
34137         }
34138         this.refreshSize();
34139         this.adjustAssets();
34140         this.focus();
34141         this.fireEvent("move", this, this.xy[0], this.xy[1]);
34142     },
34143
34144     /**
34145      * Brings this dialog to the front of any other visible dialogs
34146      * @return {Roo.BasicDialog} this
34147      */
34148     toFront : function(){
34149         Roo.DialogManager.bringToFront(this);
34150         return this;
34151     },
34152
34153     /**
34154      * Sends this dialog to the back (under) of any other visible dialogs
34155      * @return {Roo.BasicDialog} this
34156      */
34157     toBack : function(){
34158         Roo.DialogManager.sendToBack(this);
34159         return this;
34160     },
34161
34162     /**
34163      * Centers this dialog in the viewport
34164      * @return {Roo.BasicDialog} this
34165      */
34166     center : function(){
34167         var xy = this.el.getCenterXY(true);
34168         this.moveTo(xy[0], xy[1]);
34169         return this;
34170     },
34171
34172     /**
34173      * Moves the dialog's top-left corner to the specified point
34174      * @param {Number} x
34175      * @param {Number} y
34176      * @return {Roo.BasicDialog} this
34177      */
34178     moveTo : function(x, y){
34179         this.xy = [x,y];
34180         if(this.isVisible()){
34181             this.el.setXY(this.xy);
34182             this.adjustAssets();
34183         }
34184         return this;
34185     },
34186
34187     /**
34188      * Aligns the dialog to the specified element
34189      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34190      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
34191      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34192      * @return {Roo.BasicDialog} this
34193      */
34194     alignTo : function(element, position, offsets){
34195         this.xy = this.el.getAlignToXY(element, position, offsets);
34196         if(this.isVisible()){
34197             this.el.setXY(this.xy);
34198             this.adjustAssets();
34199         }
34200         return this;
34201     },
34202
34203     /**
34204      * Anchors an element to another element and realigns it when the window is resized.
34205      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34206      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
34207      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34208      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
34209      * is a number, it is used as the buffer delay (defaults to 50ms).
34210      * @return {Roo.BasicDialog} this
34211      */
34212     anchorTo : function(el, alignment, offsets, monitorScroll){
34213         var action = function(){
34214             this.alignTo(el, alignment, offsets);
34215         };
34216         Roo.EventManager.onWindowResize(action, this);
34217         var tm = typeof monitorScroll;
34218         if(tm != 'undefined'){
34219             Roo.EventManager.on(window, 'scroll', action, this,
34220                 {buffer: tm == 'number' ? monitorScroll : 50});
34221         }
34222         action.call(this);
34223         return this;
34224     },
34225
34226     /**
34227      * Returns true if the dialog is visible
34228      * @return {Boolean}
34229      */
34230     isVisible : function(){
34231         return this.el.isVisible();
34232     },
34233
34234     // private
34235     animHide : function(callback){
34236         var b = Roo.get(this.animateTarget).getBox();
34237         this.proxy.show();
34238         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
34239         this.el.hide();
34240         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
34241                     this.hideEl.createDelegate(this, [callback]));
34242     },
34243
34244     /**
34245      * Hides the dialog.
34246      * @param {Function} callback (optional) Function to call when the dialog is hidden
34247      * @return {Roo.BasicDialog} this
34248      */
34249     hide : function(callback){
34250         if (this.fireEvent("beforehide", this) === false){
34251             return;
34252         }
34253         if(this.shadow){
34254             this.shadow.hide();
34255         }
34256         if(this.shim) {
34257           this.shim.hide();
34258         }
34259         // sometimes animateTarget seems to get set.. causing problems...
34260         // this just double checks..
34261         if(this.animateTarget && Roo.get(this.animateTarget)) {
34262            this.animHide(callback);
34263         }else{
34264             this.el.hide();
34265             this.hideEl(callback);
34266         }
34267         return this;
34268     },
34269
34270     // private
34271     hideEl : function(callback){
34272         this.proxy.hide();
34273         if(this.modal){
34274             this.mask.hide();
34275             Roo.get(document.body).removeClass("x-body-masked");
34276         }
34277         this.fireEvent("hide", this);
34278         if(typeof callback == "function"){
34279             callback();
34280         }
34281     },
34282
34283     // private
34284     hideAction : function(){
34285         this.setLeft("-10000px");
34286         this.setTop("-10000px");
34287         this.setStyle("visibility", "hidden");
34288     },
34289
34290     // private
34291     refreshSize : function(){
34292         this.size = this.el.getSize();
34293         this.xy = this.el.getXY();
34294         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
34295     },
34296
34297     // private
34298     // z-index is managed by the DialogManager and may be overwritten at any time
34299     setZIndex : function(index){
34300         if(this.modal){
34301             this.mask.setStyle("z-index", index);
34302         }
34303         if(this.shim){
34304             this.shim.setStyle("z-index", ++index);
34305         }
34306         if(this.shadow){
34307             this.shadow.setZIndex(++index);
34308         }
34309         this.el.setStyle("z-index", ++index);
34310         if(this.proxy){
34311             this.proxy.setStyle("z-index", ++index);
34312         }
34313         if(this.resizer){
34314             this.resizer.proxy.setStyle("z-index", ++index);
34315         }
34316
34317         this.lastZIndex = index;
34318     },
34319
34320     /**
34321      * Returns the element for this dialog
34322      * @return {Roo.Element} The underlying dialog Element
34323      */
34324     getEl : function(){
34325         return this.el;
34326     }
34327 });
34328
34329 /**
34330  * @class Roo.DialogManager
34331  * Provides global access to BasicDialogs that have been created and
34332  * support for z-indexing (layering) multiple open dialogs.
34333  */
34334 Roo.DialogManager = function(){
34335     var list = {};
34336     var accessList = [];
34337     var front = null;
34338
34339     // private
34340     var sortDialogs = function(d1, d2){
34341         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34342     };
34343
34344     // private
34345     var orderDialogs = function(){
34346         accessList.sort(sortDialogs);
34347         var seed = Roo.DialogManager.zseed;
34348         for(var i = 0, len = accessList.length; i < len; i++){
34349             var dlg = accessList[i];
34350             if(dlg){
34351                 dlg.setZIndex(seed + (i*10));
34352             }
34353         }
34354     };
34355
34356     return {
34357         /**
34358          * The starting z-index for BasicDialogs (defaults to 9000)
34359          * @type Number The z-index value
34360          */
34361         zseed : 9000,
34362
34363         // private
34364         register : function(dlg){
34365             list[dlg.id] = dlg;
34366             accessList.push(dlg);
34367         },
34368
34369         // private
34370         unregister : function(dlg){
34371             delete list[dlg.id];
34372             var i=0;
34373             var len=0;
34374             if(!accessList.indexOf){
34375                 for(  i = 0, len = accessList.length; i < len; i++){
34376                     if(accessList[i] == dlg){
34377                         accessList.splice(i, 1);
34378                         return;
34379                     }
34380                 }
34381             }else{
34382                  i = accessList.indexOf(dlg);
34383                 if(i != -1){
34384                     accessList.splice(i, 1);
34385                 }
34386             }
34387         },
34388
34389         /**
34390          * Gets a registered dialog by id
34391          * @param {String/Object} id The id of the dialog or a dialog
34392          * @return {Roo.BasicDialog} this
34393          */
34394         get : function(id){
34395             return typeof id == "object" ? id : list[id];
34396         },
34397
34398         /**
34399          * Brings the specified dialog to the front
34400          * @param {String/Object} dlg The id of the dialog or a dialog
34401          * @return {Roo.BasicDialog} this
34402          */
34403         bringToFront : function(dlg){
34404             dlg = this.get(dlg);
34405             if(dlg != front){
34406                 front = dlg;
34407                 dlg._lastAccess = new Date().getTime();
34408                 orderDialogs();
34409             }
34410             return dlg;
34411         },
34412
34413         /**
34414          * Sends the specified dialog to the back
34415          * @param {String/Object} dlg The id of the dialog or a dialog
34416          * @return {Roo.BasicDialog} this
34417          */
34418         sendToBack : function(dlg){
34419             dlg = this.get(dlg);
34420             dlg._lastAccess = -(new Date().getTime());
34421             orderDialogs();
34422             return dlg;
34423         },
34424
34425         /**
34426          * Hides all dialogs
34427          */
34428         hideAll : function(){
34429             for(var id in list){
34430                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34431                     list[id].hide();
34432                 }
34433             }
34434         }
34435     };
34436 }();
34437
34438 /**
34439  * @class Roo.LayoutDialog
34440  * @extends Roo.BasicDialog
34441  * @children Roo.ContentPanel
34442  * @parent builder none
34443  * Dialog which provides adjustments for working with a layout in a Dialog.
34444  * Add your necessary layout config options to the dialog's config.<br>
34445  * Example usage (including a nested layout):
34446  * <pre><code>
34447 if(!dialog){
34448     dialog = new Roo.LayoutDialog("download-dlg", {
34449         modal: true,
34450         width:600,
34451         height:450,
34452         shadow:true,
34453         minWidth:500,
34454         minHeight:350,
34455         autoTabs:true,
34456         proxyDrag:true,
34457         // layout config merges with the dialog config
34458         center:{
34459             tabPosition: "top",
34460             alwaysShowTabs: true
34461         }
34462     });
34463     dialog.addKeyListener(27, dialog.hide, dialog);
34464     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34465     dialog.addButton("Build It!", this.getDownload, this);
34466
34467     // we can even add nested layouts
34468     var innerLayout = new Roo.BorderLayout("dl-inner", {
34469         east: {
34470             initialSize: 200,
34471             autoScroll:true,
34472             split:true
34473         },
34474         center: {
34475             autoScroll:true
34476         }
34477     });
34478     innerLayout.beginUpdate();
34479     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34480     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34481     innerLayout.endUpdate(true);
34482
34483     var layout = dialog.getLayout();
34484     layout.beginUpdate();
34485     layout.add("center", new Roo.ContentPanel("standard-panel",
34486                         {title: "Download the Source", fitToFrame:true}));
34487     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34488                {title: "Build your own roo.js"}));
34489     layout.getRegion("center").showPanel(sp);
34490     layout.endUpdate();
34491 }
34492 </code></pre>
34493     * @constructor
34494     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34495     * @param {Object} config configuration options
34496   */
34497 Roo.LayoutDialog = function(el, cfg){
34498     
34499     var config=  cfg;
34500     if (typeof(cfg) == 'undefined') {
34501         config = Roo.apply({}, el);
34502         // not sure why we use documentElement here.. - it should always be body.
34503         // IE7 borks horribly if we use documentElement.
34504         // webkit also does not like documentElement - it creates a body element...
34505         el = Roo.get( document.body || document.documentElement ).createChild();
34506         //config.autoCreate = true;
34507     }
34508     
34509     
34510     config.autoTabs = false;
34511     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34512     this.body.setStyle({overflow:"hidden", position:"relative"});
34513     this.layout = new Roo.BorderLayout(this.body.dom, config);
34514     this.layout.monitorWindowResize = false;
34515     this.el.addClass("x-dlg-auto-layout");
34516     // fix case when center region overwrites center function
34517     this.center = Roo.BasicDialog.prototype.center;
34518     this.on("show", this.layout.layout, this.layout, true);
34519     if (config.items) {
34520         var xitems = config.items;
34521         delete config.items;
34522         Roo.each(xitems, this.addxtype, this);
34523     }
34524     
34525     
34526 };
34527 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34528     
34529     
34530     /**
34531      * @cfg {Roo.LayoutRegion} east  
34532      */
34533     /**
34534      * @cfg {Roo.LayoutRegion} west
34535      */
34536     /**
34537      * @cfg {Roo.LayoutRegion} south
34538      */
34539     /**
34540      * @cfg {Roo.LayoutRegion} north
34541      */
34542     /**
34543      * @cfg {Roo.LayoutRegion} center
34544      */
34545     /**
34546      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34547      */
34548     
34549     
34550     /**
34551      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34552      * @deprecated
34553      */
34554     endUpdate : function(){
34555         this.layout.endUpdate();
34556     },
34557
34558     /**
34559      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34560      *  @deprecated
34561      */
34562     beginUpdate : function(){
34563         this.layout.beginUpdate();
34564     },
34565
34566     /**
34567      * Get the BorderLayout for this dialog
34568      * @return {Roo.BorderLayout}
34569      */
34570     getLayout : function(){
34571         return this.layout;
34572     },
34573
34574     showEl : function(){
34575         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34576         if(Roo.isIE7){
34577             this.layout.layout();
34578         }
34579     },
34580
34581     // private
34582     // Use the syncHeightBeforeShow config option to control this automatically
34583     syncBodyHeight : function(){
34584         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34585         if(this.layout){this.layout.layout();}
34586     },
34587     
34588       /**
34589      * Add an xtype element (actually adds to the layout.)
34590      * @return {Object} xdata xtype object data.
34591      */
34592     
34593     addxtype : function(c) {
34594         return this.layout.addxtype(c);
34595     }
34596 });/*
34597  * Based on:
34598  * Ext JS Library 1.1.1
34599  * Copyright(c) 2006-2007, Ext JS, LLC.
34600  *
34601  * Originally Released Under LGPL - original licence link has changed is not relivant.
34602  *
34603  * Fork - LGPL
34604  * <script type="text/javascript">
34605  */
34606  
34607 /**
34608  * @class Roo.MessageBox
34609  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34610  * Example usage:
34611  *<pre><code>
34612 // Basic alert:
34613 Roo.Msg.alert('Status', 'Changes saved successfully.');
34614
34615 // Prompt for user data:
34616 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34617     if (btn == 'ok'){
34618         // process text value...
34619     }
34620 });
34621
34622 // Show a dialog using config options:
34623 Roo.Msg.show({
34624    title:'Save Changes?',
34625    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34626    buttons: Roo.Msg.YESNOCANCEL,
34627    fn: processResult,
34628    animEl: 'elId'
34629 });
34630 </code></pre>
34631  * @static
34632  */
34633 Roo.MessageBox = function(){
34634     var dlg, opt, mask, waitTimer;
34635     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34636     var buttons, activeTextEl, bwidth;
34637
34638     // private
34639     var handleButton = function(button){
34640         dlg.hide();
34641         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34642     };
34643
34644     // private
34645     var handleHide = function(){
34646         if(opt && opt.cls){
34647             dlg.el.removeClass(opt.cls);
34648         }
34649         if(waitTimer){
34650             Roo.TaskMgr.stop(waitTimer);
34651             waitTimer = null;
34652         }
34653     };
34654
34655     // private
34656     var updateButtons = function(b){
34657         var width = 0;
34658         if(!b){
34659             buttons["ok"].hide();
34660             buttons["cancel"].hide();
34661             buttons["yes"].hide();
34662             buttons["no"].hide();
34663             dlg.footer.dom.style.display = 'none';
34664             return width;
34665         }
34666         dlg.footer.dom.style.display = '';
34667         for(var k in buttons){
34668             if(typeof buttons[k] != "function"){
34669                 if(b[k]){
34670                     buttons[k].show();
34671                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34672                     width += buttons[k].el.getWidth()+15;
34673                 }else{
34674                     buttons[k].hide();
34675                 }
34676             }
34677         }
34678         return width;
34679     };
34680
34681     // private
34682     var handleEsc = function(d, k, e){
34683         if(opt && opt.closable !== false){
34684             dlg.hide();
34685         }
34686         if(e){
34687             e.stopEvent();
34688         }
34689     };
34690
34691     return {
34692         /**
34693          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34694          * @return {Roo.BasicDialog} The BasicDialog element
34695          */
34696         getDialog : function(){
34697            if(!dlg){
34698                 dlg = new Roo.BasicDialog("x-msg-box", {
34699                     autoCreate : true,
34700                     shadow: true,
34701                     draggable: true,
34702                     resizable:false,
34703                     constraintoviewport:false,
34704                     fixedcenter:true,
34705                     collapsible : false,
34706                     shim:true,
34707                     modal: true,
34708                     width:400, height:100,
34709                     buttonAlign:"center",
34710                     closeClick : function(){
34711                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34712                             handleButton("no");
34713                         }else{
34714                             handleButton("cancel");
34715                         }
34716                     }
34717                 });
34718                 dlg.on("hide", handleHide);
34719                 mask = dlg.mask;
34720                 dlg.addKeyListener(27, handleEsc);
34721                 buttons = {};
34722                 var bt = this.buttonText;
34723                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34724                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34725                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34726                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34727                 bodyEl = dlg.body.createChild({
34728
34729                     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>'
34730                 });
34731                 msgEl = bodyEl.dom.firstChild;
34732                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34733                 textboxEl.enableDisplayMode();
34734                 textboxEl.addKeyListener([10,13], function(){
34735                     if(dlg.isVisible() && opt && opt.buttons){
34736                         if(opt.buttons.ok){
34737                             handleButton("ok");
34738                         }else if(opt.buttons.yes){
34739                             handleButton("yes");
34740                         }
34741                     }
34742                 });
34743                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34744                 textareaEl.enableDisplayMode();
34745                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34746                 progressEl.enableDisplayMode();
34747                 var pf = progressEl.dom.firstChild;
34748                 if (pf) {
34749                     pp = Roo.get(pf.firstChild);
34750                     pp.setHeight(pf.offsetHeight);
34751                 }
34752                 
34753             }
34754             return dlg;
34755         },
34756
34757         /**
34758          * Updates the message box body text
34759          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34760          * the XHTML-compliant non-breaking space character '&amp;#160;')
34761          * @return {Roo.MessageBox} This message box
34762          */
34763         updateText : function(text){
34764             if(!dlg.isVisible() && !opt.width){
34765                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34766             }
34767             msgEl.innerHTML = text || '&#160;';
34768       
34769             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34770             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34771             var w = Math.max(
34772                     Math.min(opt.width || cw , this.maxWidth), 
34773                     Math.max(opt.minWidth || this.minWidth, bwidth)
34774             );
34775             if(opt.prompt){
34776                 activeTextEl.setWidth(w);
34777             }
34778             if(dlg.isVisible()){
34779                 dlg.fixedcenter = false;
34780             }
34781             // to big, make it scroll. = But as usual stupid IE does not support
34782             // !important..
34783             
34784             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34785                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34786                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34787             } else {
34788                 bodyEl.dom.style.height = '';
34789                 bodyEl.dom.style.overflowY = '';
34790             }
34791             if (cw > w) {
34792                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34793             } else {
34794                 bodyEl.dom.style.overflowX = '';
34795             }
34796             
34797             dlg.setContentSize(w, bodyEl.getHeight());
34798             if(dlg.isVisible()){
34799                 dlg.fixedcenter = true;
34800             }
34801             return this;
34802         },
34803
34804         /**
34805          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34806          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34807          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34808          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34809          * @return {Roo.MessageBox} This message box
34810          */
34811         updateProgress : function(value, text){
34812             if(text){
34813                 this.updateText(text);
34814             }
34815             if (pp) { // weird bug on my firefox - for some reason this is not defined
34816                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34817             }
34818             return this;
34819         },        
34820
34821         /**
34822          * Returns true if the message box is currently displayed
34823          * @return {Boolean} True if the message box is visible, else false
34824          */
34825         isVisible : function(){
34826             return dlg && dlg.isVisible();  
34827         },
34828
34829         /**
34830          * Hides the message box if it is displayed
34831          */
34832         hide : function(){
34833             if(this.isVisible()){
34834                 dlg.hide();
34835             }  
34836         },
34837
34838         /**
34839          * Displays a new message box, or reinitializes an existing message box, based on the config options
34840          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34841          * The following config object properties are supported:
34842          * <pre>
34843 Property    Type             Description
34844 ----------  ---------------  ------------------------------------------------------------------------------------
34845 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34846                                    closes (defaults to undefined)
34847 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34848                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34849 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34850                                    progress and wait dialogs will ignore this property and always hide the
34851                                    close button as they can only be closed programmatically.
34852 cls               String           A custom CSS class to apply to the message box element
34853 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34854                                    displayed (defaults to 75)
34855 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34856                                    function will be btn (the name of the button that was clicked, if applicable,
34857                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34858                                    Progress and wait dialogs will ignore this option since they do not respond to
34859                                    user actions and can only be closed programmatically, so any required function
34860                                    should be called by the same code after it closes the dialog.
34861 icon              String           A CSS class that provides a background image to be used as an icon for
34862                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34863 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34864 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34865 modal             Boolean          False to allow user interaction with the page while the message box is
34866                                    displayed (defaults to true)
34867 msg               String           A string that will replace the existing message box body text (defaults
34868                                    to the XHTML-compliant non-breaking space character '&#160;')
34869 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34870 progress          Boolean          True to display a progress bar (defaults to false)
34871 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34872 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34873 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34874 title             String           The title text
34875 value             String           The string value to set into the active textbox element if displayed
34876 wait              Boolean          True to display a progress bar (defaults to false)
34877 width             Number           The width of the dialog in pixels
34878 </pre>
34879          *
34880          * Example usage:
34881          * <pre><code>
34882 Roo.Msg.show({
34883    title: 'Address',
34884    msg: 'Please enter your address:',
34885    width: 300,
34886    buttons: Roo.MessageBox.OKCANCEL,
34887    multiline: true,
34888    fn: saveAddress,
34889    animEl: 'addAddressBtn'
34890 });
34891 </code></pre>
34892          * @param {Object} config Configuration options
34893          * @return {Roo.MessageBox} This message box
34894          */
34895         show : function(options)
34896         {
34897             
34898             // this causes nightmares if you show one dialog after another
34899             // especially on callbacks..
34900              
34901             if(this.isVisible()){
34902                 
34903                 this.hide();
34904                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34905                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34906                 Roo.log("New Dialog Message:" +  options.msg )
34907                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34908                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34909                 
34910             }
34911             var d = this.getDialog();
34912             opt = options;
34913             d.setTitle(opt.title || "&#160;");
34914             d.close.setDisplayed(opt.closable !== false);
34915             activeTextEl = textboxEl;
34916             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34917             if(opt.prompt){
34918                 if(opt.multiline){
34919                     textboxEl.hide();
34920                     textareaEl.show();
34921                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34922                         opt.multiline : this.defaultTextHeight);
34923                     activeTextEl = textareaEl;
34924                 }else{
34925                     textboxEl.show();
34926                     textareaEl.hide();
34927                 }
34928             }else{
34929                 textboxEl.hide();
34930                 textareaEl.hide();
34931             }
34932             progressEl.setDisplayed(opt.progress === true);
34933             this.updateProgress(0);
34934             activeTextEl.dom.value = opt.value || "";
34935             if(opt.prompt){
34936                 dlg.setDefaultButton(activeTextEl);
34937             }else{
34938                 var bs = opt.buttons;
34939                 var db = null;
34940                 if(bs && bs.ok){
34941                     db = buttons["ok"];
34942                 }else if(bs && bs.yes){
34943                     db = buttons["yes"];
34944                 }
34945                 dlg.setDefaultButton(db);
34946             }
34947             bwidth = updateButtons(opt.buttons);
34948             this.updateText(opt.msg);
34949             if(opt.cls){
34950                 d.el.addClass(opt.cls);
34951             }
34952             d.proxyDrag = opt.proxyDrag === true;
34953             d.modal = opt.modal !== false;
34954             d.mask = opt.modal !== false ? mask : false;
34955             if(!d.isVisible()){
34956                 // force it to the end of the z-index stack so it gets a cursor in FF
34957                 document.body.appendChild(dlg.el.dom);
34958                 d.animateTarget = null;
34959                 d.show(options.animEl);
34960             }
34961             return this;
34962         },
34963
34964         /**
34965          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34966          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34967          * and closing the message box when the process is complete.
34968          * @param {String} title The title bar text
34969          * @param {String} msg The message box body text
34970          * @return {Roo.MessageBox} This message box
34971          */
34972         progress : function(title, msg){
34973             this.show({
34974                 title : title,
34975                 msg : msg,
34976                 buttons: false,
34977                 progress:true,
34978                 closable:false,
34979                 minWidth: this.minProgressWidth,
34980                 modal : true
34981             });
34982             return this;
34983         },
34984
34985         /**
34986          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34987          * If a callback function is passed it will be called after the user clicks the button, and the
34988          * id of the button that was clicked will be passed as the only parameter to the callback
34989          * (could also be the top-right close button).
34990          * @param {String} title The title bar text
34991          * @param {String} msg The message box body text
34992          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34993          * @param {Object} scope (optional) The scope of the callback function
34994          * @return {Roo.MessageBox} This message box
34995          */
34996         alert : function(title, msg, fn, scope){
34997             this.show({
34998                 title : title,
34999                 msg : msg,
35000                 buttons: this.OK,
35001                 fn: fn,
35002                 scope : scope,
35003                 modal : true
35004             });
35005             return this;
35006         },
35007
35008         /**
35009          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
35010          * interaction while waiting for a long-running process to complete that does not have defined intervals.
35011          * You are responsible for closing the message box when the process is complete.
35012          * @param {String} msg The message box body text
35013          * @param {String} title (optional) The title bar text
35014          * @return {Roo.MessageBox} This message box
35015          */
35016         wait : function(msg, title){
35017             this.show({
35018                 title : title,
35019                 msg : msg,
35020                 buttons: false,
35021                 closable:false,
35022                 progress:true,
35023                 modal:true,
35024                 width:300,
35025                 wait:true
35026             });
35027             waitTimer = Roo.TaskMgr.start({
35028                 run: function(i){
35029                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
35030                 },
35031                 interval: 1000
35032             });
35033             return this;
35034         },
35035
35036         /**
35037          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
35038          * If a callback function is passed it will be called after the user clicks either button, and the id of the
35039          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
35040          * @param {String} title The title bar text
35041          * @param {String} msg The message box body text
35042          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35043          * @param {Object} scope (optional) The scope of the callback function
35044          * @return {Roo.MessageBox} This message box
35045          */
35046         confirm : function(title, msg, fn, scope){
35047             this.show({
35048                 title : title,
35049                 msg : msg,
35050                 buttons: this.YESNO,
35051                 fn: fn,
35052                 scope : scope,
35053                 modal : true
35054             });
35055             return this;
35056         },
35057
35058         /**
35059          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
35060          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
35061          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
35062          * (could also be the top-right close button) and the text that was entered will be passed as the two
35063          * parameters to the callback.
35064          * @param {String} title The title bar text
35065          * @param {String} msg The message box body text
35066          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35067          * @param {Object} scope (optional) The scope of the callback function
35068          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
35069          * property, or the height in pixels to create the textbox (defaults to false / single-line)
35070          * @return {Roo.MessageBox} This message box
35071          */
35072         prompt : function(title, msg, fn, scope, multiline){
35073             this.show({
35074                 title : title,
35075                 msg : msg,
35076                 buttons: this.OKCANCEL,
35077                 fn: fn,
35078                 minWidth:250,
35079                 scope : scope,
35080                 prompt:true,
35081                 multiline: multiline,
35082                 modal : true
35083             });
35084             return this;
35085         },
35086
35087         /**
35088          * Button config that displays a single OK button
35089          * @type Object
35090          */
35091         OK : {ok:true},
35092         /**
35093          * Button config that displays Yes and No buttons
35094          * @type Object
35095          */
35096         YESNO : {yes:true, no:true},
35097         /**
35098          * Button config that displays OK and Cancel buttons
35099          * @type Object
35100          */
35101         OKCANCEL : {ok:true, cancel:true},
35102         /**
35103          * Button config that displays Yes, No and Cancel buttons
35104          * @type Object
35105          */
35106         YESNOCANCEL : {yes:true, no:true, cancel:true},
35107
35108         /**
35109          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
35110          * @type Number
35111          */
35112         defaultTextHeight : 75,
35113         /**
35114          * The maximum width in pixels of the message box (defaults to 600)
35115          * @type Number
35116          */
35117         maxWidth : 600,
35118         /**
35119          * The minimum width in pixels of the message box (defaults to 100)
35120          * @type Number
35121          */
35122         minWidth : 100,
35123         /**
35124          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
35125          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
35126          * @type Number
35127          */
35128         minProgressWidth : 250,
35129         /**
35130          * An object containing the default button text strings that can be overriden for localized language support.
35131          * Supported properties are: ok, cancel, yes and no.
35132          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
35133          * @type Object
35134          */
35135         buttonText : {
35136             ok : "OK",
35137             cancel : "Cancel",
35138             yes : "Yes",
35139             no : "No"
35140         }
35141     };
35142 }();
35143
35144 /**
35145  * Shorthand for {@link Roo.MessageBox}
35146  */
35147 Roo.Msg = Roo.MessageBox;/*
35148  * Based on:
35149  * Ext JS Library 1.1.1
35150  * Copyright(c) 2006-2007, Ext JS, LLC.
35151  *
35152  * Originally Released Under LGPL - original licence link has changed is not relivant.
35153  *
35154  * Fork - LGPL
35155  * <script type="text/javascript">
35156  */
35157 /**
35158  * @class Roo.QuickTips
35159  * Provides attractive and customizable tooltips for any element.
35160  * @static
35161  */
35162 Roo.QuickTips = function(){
35163     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
35164     var ce, bd, xy, dd;
35165     var visible = false, disabled = true, inited = false;
35166     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
35167     
35168     var onOver = function(e){
35169         if(disabled){
35170             return;
35171         }
35172         var t = e.getTarget();
35173         if(!t || t.nodeType !== 1 || t == document || t == document.body){
35174             return;
35175         }
35176         if(ce && t == ce.el){
35177             clearTimeout(hideProc);
35178             return;
35179         }
35180         if(t && tagEls[t.id]){
35181             tagEls[t.id].el = t;
35182             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
35183             return;
35184         }
35185         var ttp, et = Roo.fly(t);
35186         var ns = cfg.namespace;
35187         if(tm.interceptTitles && t.title){
35188             ttp = t.title;
35189             t.qtip = ttp;
35190             t.removeAttribute("title");
35191             e.preventDefault();
35192         }else{
35193             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
35194         }
35195         if(ttp){
35196             showProc = show.defer(tm.showDelay, tm, [{
35197                 el: t, 
35198                 text: ttp.replace(/\\n/g,'<br/>'),
35199                 width: et.getAttributeNS(ns, cfg.width),
35200                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
35201                 title: et.getAttributeNS(ns, cfg.title),
35202                     cls: et.getAttributeNS(ns, cfg.cls)
35203             }]);
35204         }
35205     };
35206     
35207     var onOut = function(e){
35208         clearTimeout(showProc);
35209         var t = e.getTarget();
35210         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
35211             hideProc = setTimeout(hide, tm.hideDelay);
35212         }
35213     };
35214     
35215     var onMove = function(e){
35216         if(disabled){
35217             return;
35218         }
35219         xy = e.getXY();
35220         xy[1] += 18;
35221         if(tm.trackMouse && ce){
35222             el.setXY(xy);
35223         }
35224     };
35225     
35226     var onDown = function(e){
35227         clearTimeout(showProc);
35228         clearTimeout(hideProc);
35229         if(!e.within(el)){
35230             if(tm.hideOnClick){
35231                 hide();
35232                 tm.disable();
35233                 tm.enable.defer(100, tm);
35234             }
35235         }
35236     };
35237     
35238     var getPad = function(){
35239         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
35240     };
35241
35242     var show = function(o){
35243         if(disabled){
35244             return;
35245         }
35246         clearTimeout(dismissProc);
35247         ce = o;
35248         if(removeCls){ // in case manually hidden
35249             el.removeClass(removeCls);
35250             removeCls = null;
35251         }
35252         if(ce.cls){
35253             el.addClass(ce.cls);
35254             removeCls = ce.cls;
35255         }
35256         if(ce.title){
35257             tipTitle.update(ce.title);
35258             tipTitle.show();
35259         }else{
35260             tipTitle.update('');
35261             tipTitle.hide();
35262         }
35263         el.dom.style.width  = tm.maxWidth+'px';
35264         //tipBody.dom.style.width = '';
35265         tipBodyText.update(o.text);
35266         var p = getPad(), w = ce.width;
35267         if(!w){
35268             var td = tipBodyText.dom;
35269             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
35270             if(aw > tm.maxWidth){
35271                 w = tm.maxWidth;
35272             }else if(aw < tm.minWidth){
35273                 w = tm.minWidth;
35274             }else{
35275                 w = aw;
35276             }
35277         }
35278         //tipBody.setWidth(w);
35279         el.setWidth(parseInt(w, 10) + p);
35280         if(ce.autoHide === false){
35281             close.setDisplayed(true);
35282             if(dd){
35283                 dd.unlock();
35284             }
35285         }else{
35286             close.setDisplayed(false);
35287             if(dd){
35288                 dd.lock();
35289             }
35290         }
35291         if(xy){
35292             el.avoidY = xy[1]-18;
35293             el.setXY(xy);
35294         }
35295         if(tm.animate){
35296             el.setOpacity(.1);
35297             el.setStyle("visibility", "visible");
35298             el.fadeIn({callback: afterShow});
35299         }else{
35300             afterShow();
35301         }
35302     };
35303     
35304     var afterShow = function(){
35305         if(ce){
35306             el.show();
35307             esc.enable();
35308             if(tm.autoDismiss && ce.autoHide !== false){
35309                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
35310             }
35311         }
35312     };
35313     
35314     var hide = function(noanim){
35315         clearTimeout(dismissProc);
35316         clearTimeout(hideProc);
35317         ce = null;
35318         if(el.isVisible()){
35319             esc.disable();
35320             if(noanim !== true && tm.animate){
35321                 el.fadeOut({callback: afterHide});
35322             }else{
35323                 afterHide();
35324             } 
35325         }
35326     };
35327     
35328     var afterHide = function(){
35329         el.hide();
35330         if(removeCls){
35331             el.removeClass(removeCls);
35332             removeCls = null;
35333         }
35334     };
35335     
35336     return {
35337         /**
35338         * @cfg {Number} minWidth
35339         * The minimum width of the quick tip (defaults to 40)
35340         */
35341        minWidth : 40,
35342         /**
35343         * @cfg {Number} maxWidth
35344         * The maximum width of the quick tip (defaults to 300)
35345         */
35346        maxWidth : 300,
35347         /**
35348         * @cfg {Boolean} interceptTitles
35349         * True to automatically use the element's DOM title value if available (defaults to false)
35350         */
35351        interceptTitles : false,
35352         /**
35353         * @cfg {Boolean} trackMouse
35354         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35355         */
35356        trackMouse : false,
35357         /**
35358         * @cfg {Boolean} hideOnClick
35359         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35360         */
35361        hideOnClick : true,
35362         /**
35363         * @cfg {Number} showDelay
35364         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35365         */
35366        showDelay : 500,
35367         /**
35368         * @cfg {Number} hideDelay
35369         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35370         */
35371        hideDelay : 200,
35372         /**
35373         * @cfg {Boolean} autoHide
35374         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35375         * Used in conjunction with hideDelay.
35376         */
35377        autoHide : true,
35378         /**
35379         * @cfg {Boolean}
35380         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35381         * (defaults to true).  Used in conjunction with autoDismissDelay.
35382         */
35383        autoDismiss : true,
35384         /**
35385         * @cfg {Number}
35386         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35387         */
35388        autoDismissDelay : 5000,
35389        /**
35390         * @cfg {Boolean} animate
35391         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35392         */
35393        animate : false,
35394
35395        /**
35396         * @cfg {String} title
35397         * Title text to display (defaults to '').  This can be any valid HTML markup.
35398         */
35399         title: '',
35400        /**
35401         * @cfg {String} text
35402         * Body text to display (defaults to '').  This can be any valid HTML markup.
35403         */
35404         text : '',
35405        /**
35406         * @cfg {String} cls
35407         * A CSS class to apply to the base quick tip element (defaults to '').
35408         */
35409         cls : '',
35410        /**
35411         * @cfg {Number} width
35412         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35413         * minWidth or maxWidth.
35414         */
35415         width : null,
35416
35417     /**
35418      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35419      * or display QuickTips in a page.
35420      */
35421        init : function(){
35422           tm = Roo.QuickTips;
35423           cfg = tm.tagConfig;
35424           if(!inited){
35425               if(!Roo.isReady){ // allow calling of init() before onReady
35426                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35427                   return;
35428               }
35429               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35430               el.fxDefaults = {stopFx: true};
35431               // maximum custom styling
35432               //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>');
35433               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>');              
35434               tipTitle = el.child('h3');
35435               tipTitle.enableDisplayMode("block");
35436               tipBody = el.child('div.x-tip-bd');
35437               tipBodyText = el.child('div.x-tip-bd-inner');
35438               //bdLeft = el.child('div.x-tip-bd-left');
35439               //bdRight = el.child('div.x-tip-bd-right');
35440               close = el.child('div.x-tip-close');
35441               close.enableDisplayMode("block");
35442               close.on("click", hide);
35443               var d = Roo.get(document);
35444               d.on("mousedown", onDown);
35445               d.on("mouseover", onOver);
35446               d.on("mouseout", onOut);
35447               d.on("mousemove", onMove);
35448               esc = d.addKeyListener(27, hide);
35449               esc.disable();
35450               if(Roo.dd.DD){
35451                   dd = el.initDD("default", null, {
35452                       onDrag : function(){
35453                           el.sync();  
35454                       }
35455                   });
35456                   dd.setHandleElId(tipTitle.id);
35457                   dd.lock();
35458               }
35459               inited = true;
35460           }
35461           this.enable(); 
35462        },
35463
35464     /**
35465      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35466      * are supported:
35467      * <pre>
35468 Property    Type                   Description
35469 ----------  ---------------------  ------------------------------------------------------------------------
35470 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35471      * </ul>
35472      * @param {Object} config The config object
35473      */
35474        register : function(config){
35475            var cs = config instanceof Array ? config : arguments;
35476            for(var i = 0, len = cs.length; i < len; i++) {
35477                var c = cs[i];
35478                var target = c.target;
35479                if(target){
35480                    if(target instanceof Array){
35481                        for(var j = 0, jlen = target.length; j < jlen; j++){
35482                            tagEls[target[j]] = c;
35483                        }
35484                    }else{
35485                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35486                    }
35487                }
35488            }
35489        },
35490
35491     /**
35492      * Removes this quick tip from its element and destroys it.
35493      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35494      */
35495        unregister : function(el){
35496            delete tagEls[Roo.id(el)];
35497        },
35498
35499     /**
35500      * Enable this quick tip.
35501      */
35502        enable : function(){
35503            if(inited && disabled){
35504                locks.pop();
35505                if(locks.length < 1){
35506                    disabled = false;
35507                }
35508            }
35509        },
35510
35511     /**
35512      * Disable this quick tip.
35513      */
35514        disable : function(){
35515           disabled = true;
35516           clearTimeout(showProc);
35517           clearTimeout(hideProc);
35518           clearTimeout(dismissProc);
35519           if(ce){
35520               hide(true);
35521           }
35522           locks.push(1);
35523        },
35524
35525     /**
35526      * Returns true if the quick tip is enabled, else false.
35527      */
35528        isEnabled : function(){
35529             return !disabled;
35530        },
35531
35532         // private
35533        tagConfig : {
35534            namespace : "roo", // was ext?? this may break..
35535            alt_namespace : "ext",
35536            attribute : "qtip",
35537            width : "width",
35538            target : "target",
35539            title : "qtitle",
35540            hide : "hide",
35541            cls : "qclass"
35542        }
35543    };
35544 }();
35545
35546 // backwards compat
35547 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35548  * Based on:
35549  * Ext JS Library 1.1.1
35550  * Copyright(c) 2006-2007, Ext JS, LLC.
35551  *
35552  * Originally Released Under LGPL - original licence link has changed is not relivant.
35553  *
35554  * Fork - LGPL
35555  * <script type="text/javascript">
35556  */
35557  
35558
35559 /**
35560  * @class Roo.tree.TreePanel
35561  * @extends Roo.data.Tree
35562  * @cfg {Roo.tree.TreeNode} root The root node
35563  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35564  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35565  * @cfg {Boolean} enableDD true to enable drag and drop
35566  * @cfg {Boolean} enableDrag true to enable just drag
35567  * @cfg {Boolean} enableDrop true to enable just drop
35568  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35569  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35570  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35571  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35572  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35573  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35574  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35575  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35576  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35577  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35578  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35579  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35580  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35581  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35582  * @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>
35583  * @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>
35584  * 
35585  * @constructor
35586  * @param {String/HTMLElement/Element} el The container element
35587  * @param {Object} config
35588  */
35589 Roo.tree.TreePanel = function(el, config){
35590     var root = false;
35591     var loader = false;
35592     if (config.root) {
35593         root = config.root;
35594         delete config.root;
35595     }
35596     if (config.loader) {
35597         loader = config.loader;
35598         delete config.loader;
35599     }
35600     
35601     Roo.apply(this, config);
35602     Roo.tree.TreePanel.superclass.constructor.call(this);
35603     this.el = Roo.get(el);
35604     this.el.addClass('x-tree');
35605     //console.log(root);
35606     if (root) {
35607         this.setRootNode( Roo.factory(root, Roo.tree));
35608     }
35609     if (loader) {
35610         this.loader = Roo.factory(loader, Roo.tree);
35611     }
35612    /**
35613     * Read-only. The id of the container element becomes this TreePanel's id.
35614     */
35615     this.id = this.el.id;
35616     this.addEvents({
35617         /**
35618         * @event beforeload
35619         * Fires before a node is loaded, return false to cancel
35620         * @param {Node} node The node being loaded
35621         */
35622         "beforeload" : true,
35623         /**
35624         * @event load
35625         * Fires when a node is loaded
35626         * @param {Node} node The node that was loaded
35627         */
35628         "load" : true,
35629         /**
35630         * @event textchange
35631         * Fires when the text for a node is changed
35632         * @param {Node} node The node
35633         * @param {String} text The new text
35634         * @param {String} oldText The old text
35635         */
35636         "textchange" : true,
35637         /**
35638         * @event beforeexpand
35639         * Fires before a node is expanded, return false to cancel.
35640         * @param {Node} node The node
35641         * @param {Boolean} deep
35642         * @param {Boolean} anim
35643         */
35644         "beforeexpand" : true,
35645         /**
35646         * @event beforecollapse
35647         * Fires before a node is collapsed, return false to cancel.
35648         * @param {Node} node The node
35649         * @param {Boolean} deep
35650         * @param {Boolean} anim
35651         */
35652         "beforecollapse" : true,
35653         /**
35654         * @event expand
35655         * Fires when a node is expanded
35656         * @param {Node} node The node
35657         */
35658         "expand" : true,
35659         /**
35660         * @event disabledchange
35661         * Fires when the disabled status of a node changes
35662         * @param {Node} node The node
35663         * @param {Boolean} disabled
35664         */
35665         "disabledchange" : true,
35666         /**
35667         * @event collapse
35668         * Fires when a node is collapsed
35669         * @param {Node} node The node
35670         */
35671         "collapse" : true,
35672         /**
35673         * @event beforeclick
35674         * Fires before click processing on a node. Return false to cancel the default action.
35675         * @param {Node} node The node
35676         * @param {Roo.EventObject} e The event object
35677         */
35678         "beforeclick":true,
35679         /**
35680         * @event checkchange
35681         * Fires when a node with a checkbox's checked property changes
35682         * @param {Node} this This node
35683         * @param {Boolean} checked
35684         */
35685         "checkchange":true,
35686         /**
35687         * @event click
35688         * Fires when a node is clicked
35689         * @param {Node} node The node
35690         * @param {Roo.EventObject} e The event object
35691         */
35692         "click":true,
35693         /**
35694         * @event dblclick
35695         * Fires when a node is double clicked
35696         * @param {Node} node The node
35697         * @param {Roo.EventObject} e The event object
35698         */
35699         "dblclick":true,
35700         /**
35701         * @event contextmenu
35702         * Fires when a node is right clicked
35703         * @param {Node} node The node
35704         * @param {Roo.EventObject} e The event object
35705         */
35706         "contextmenu":true,
35707         /**
35708         * @event beforechildrenrendered
35709         * Fires right before the child nodes for a node are rendered
35710         * @param {Node} node The node
35711         */
35712         "beforechildrenrendered":true,
35713         /**
35714         * @event startdrag
35715         * Fires when a node starts being dragged
35716         * @param {Roo.tree.TreePanel} this
35717         * @param {Roo.tree.TreeNode} node
35718         * @param {event} e The raw browser event
35719         */ 
35720        "startdrag" : true,
35721        /**
35722         * @event enddrag
35723         * Fires when a drag operation is complete
35724         * @param {Roo.tree.TreePanel} this
35725         * @param {Roo.tree.TreeNode} node
35726         * @param {event} e The raw browser event
35727         */
35728        "enddrag" : true,
35729        /**
35730         * @event dragdrop
35731         * Fires when a dragged node is dropped on a valid DD target
35732         * @param {Roo.tree.TreePanel} this
35733         * @param {Roo.tree.TreeNode} node
35734         * @param {DD} dd The dd it was dropped on
35735         * @param {event} e The raw browser event
35736         */
35737        "dragdrop" : true,
35738        /**
35739         * @event beforenodedrop
35740         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35741         * passed to handlers has the following properties:<br />
35742         * <ul style="padding:5px;padding-left:16px;">
35743         * <li>tree - The TreePanel</li>
35744         * <li>target - The node being targeted for the drop</li>
35745         * <li>data - The drag data from the drag source</li>
35746         * <li>point - The point of the drop - append, above or below</li>
35747         * <li>source - The drag source</li>
35748         * <li>rawEvent - Raw mouse event</li>
35749         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35750         * to be inserted by setting them on this object.</li>
35751         * <li>cancel - Set this to true to cancel the drop.</li>
35752         * </ul>
35753         * @param {Object} dropEvent
35754         */
35755        "beforenodedrop" : true,
35756        /**
35757         * @event nodedrop
35758         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35759         * passed to handlers has the following properties:<br />
35760         * <ul style="padding:5px;padding-left:16px;">
35761         * <li>tree - The TreePanel</li>
35762         * <li>target - The node being targeted for the drop</li>
35763         * <li>data - The drag data from the drag source</li>
35764         * <li>point - The point of the drop - append, above or below</li>
35765         * <li>source - The drag source</li>
35766         * <li>rawEvent - Raw mouse event</li>
35767         * <li>dropNode - Dropped node(s).</li>
35768         * </ul>
35769         * @param {Object} dropEvent
35770         */
35771        "nodedrop" : true,
35772         /**
35773         * @event nodedragover
35774         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35775         * passed to handlers has the following properties:<br />
35776         * <ul style="padding:5px;padding-left:16px;">
35777         * <li>tree - The TreePanel</li>
35778         * <li>target - The node being targeted for the drop</li>
35779         * <li>data - The drag data from the drag source</li>
35780         * <li>point - The point of the drop - append, above or below</li>
35781         * <li>source - The drag source</li>
35782         * <li>rawEvent - Raw mouse event</li>
35783         * <li>dropNode - Drop node(s) provided by the source.</li>
35784         * <li>cancel - Set this to true to signal drop not allowed.</li>
35785         * </ul>
35786         * @param {Object} dragOverEvent
35787         */
35788        "nodedragover" : true,
35789        /**
35790         * @event appendnode
35791         * Fires when append node to the tree
35792         * @param {Roo.tree.TreePanel} this
35793         * @param {Roo.tree.TreeNode} node
35794         * @param {Number} index The index of the newly appended node
35795         */
35796        "appendnode" : true
35797         
35798     });
35799     if(this.singleExpand){
35800        this.on("beforeexpand", this.restrictExpand, this);
35801     }
35802     if (this.editor) {
35803         this.editor.tree = this;
35804         this.editor = Roo.factory(this.editor, Roo.tree);
35805     }
35806     
35807     if (this.selModel) {
35808         this.selModel = Roo.factory(this.selModel, Roo.tree);
35809     }
35810    
35811 };
35812 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35813     rootVisible : true,
35814     animate: Roo.enableFx,
35815     lines : true,
35816     enableDD : false,
35817     hlDrop : Roo.enableFx,
35818   
35819     renderer: false,
35820     
35821     rendererTip: false,
35822     // private
35823     restrictExpand : function(node){
35824         var p = node.parentNode;
35825         if(p){
35826             if(p.expandedChild && p.expandedChild.parentNode == p){
35827                 p.expandedChild.collapse();
35828             }
35829             p.expandedChild = node;
35830         }
35831     },
35832
35833     // private override
35834     setRootNode : function(node){
35835         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35836         if(!this.rootVisible){
35837             node.ui = new Roo.tree.RootTreeNodeUI(node);
35838         }
35839         return node;
35840     },
35841
35842     /**
35843      * Returns the container element for this TreePanel
35844      */
35845     getEl : function(){
35846         return this.el;
35847     },
35848
35849     /**
35850      * Returns the default TreeLoader for this TreePanel
35851      */
35852     getLoader : function(){
35853         return this.loader;
35854     },
35855
35856     /**
35857      * Expand all nodes
35858      */
35859     expandAll : function(){
35860         this.root.expand(true);
35861     },
35862
35863     /**
35864      * Collapse all nodes
35865      */
35866     collapseAll : function(){
35867         this.root.collapse(true);
35868     },
35869
35870     /**
35871      * Returns the selection model used by this TreePanel
35872      */
35873     getSelectionModel : function(){
35874         if(!this.selModel){
35875             this.selModel = new Roo.tree.DefaultSelectionModel();
35876         }
35877         return this.selModel;
35878     },
35879
35880     /**
35881      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35882      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35883      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35884      * @return {Array}
35885      */
35886     getChecked : function(a, startNode){
35887         startNode = startNode || this.root;
35888         var r = [];
35889         var f = function(){
35890             if(this.attributes.checked){
35891                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35892             }
35893         }
35894         startNode.cascade(f);
35895         return r;
35896     },
35897
35898     /**
35899      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35900      * @param {String} path
35901      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35902      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35903      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35904      */
35905     expandPath : function(path, attr, callback){
35906         attr = attr || "id";
35907         var keys = path.split(this.pathSeparator);
35908         var curNode = this.root;
35909         if(curNode.attributes[attr] != keys[1]){ // invalid root
35910             if(callback){
35911                 callback(false, null);
35912             }
35913             return;
35914         }
35915         var index = 1;
35916         var f = function(){
35917             if(++index == keys.length){
35918                 if(callback){
35919                     callback(true, curNode);
35920                 }
35921                 return;
35922             }
35923             var c = curNode.findChild(attr, keys[index]);
35924             if(!c){
35925                 if(callback){
35926                     callback(false, curNode);
35927                 }
35928                 return;
35929             }
35930             curNode = c;
35931             c.expand(false, false, f);
35932         };
35933         curNode.expand(false, false, f);
35934     },
35935
35936     /**
35937      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35938      * @param {String} path
35939      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35940      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35941      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35942      */
35943     selectPath : function(path, attr, callback){
35944         attr = attr || "id";
35945         var keys = path.split(this.pathSeparator);
35946         var v = keys.pop();
35947         if(keys.length > 0){
35948             var f = function(success, node){
35949                 if(success && node){
35950                     var n = node.findChild(attr, v);
35951                     if(n){
35952                         n.select();
35953                         if(callback){
35954                             callback(true, n);
35955                         }
35956                     }else if(callback){
35957                         callback(false, n);
35958                     }
35959                 }else{
35960                     if(callback){
35961                         callback(false, n);
35962                     }
35963                 }
35964             };
35965             this.expandPath(keys.join(this.pathSeparator), attr, f);
35966         }else{
35967             this.root.select();
35968             if(callback){
35969                 callback(true, this.root);
35970             }
35971         }
35972     },
35973
35974     getTreeEl : function(){
35975         return this.el;
35976     },
35977
35978     /**
35979      * Trigger rendering of this TreePanel
35980      */
35981     render : function(){
35982         if (this.innerCt) {
35983             return this; // stop it rendering more than once!!
35984         }
35985         
35986         this.innerCt = this.el.createChild({tag:"ul",
35987                cls:"x-tree-root-ct " +
35988                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35989
35990         if(this.containerScroll){
35991             Roo.dd.ScrollManager.register(this.el);
35992         }
35993         if((this.enableDD || this.enableDrop) && !this.dropZone){
35994            /**
35995             * The dropZone used by this tree if drop is enabled
35996             * @type Roo.tree.TreeDropZone
35997             */
35998              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35999                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
36000            });
36001         }
36002         if((this.enableDD || this.enableDrag) && !this.dragZone){
36003            /**
36004             * The dragZone used by this tree if drag is enabled
36005             * @type Roo.tree.TreeDragZone
36006             */
36007             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
36008                ddGroup: this.ddGroup || "TreeDD",
36009                scroll: this.ddScroll
36010            });
36011         }
36012         this.getSelectionModel().init(this);
36013         if (!this.root) {
36014             Roo.log("ROOT not set in tree");
36015             return this;
36016         }
36017         this.root.render();
36018         if(!this.rootVisible){
36019             this.root.renderChildren();
36020         }
36021         return this;
36022     }
36023 });/*
36024  * Based on:
36025  * Ext JS Library 1.1.1
36026  * Copyright(c) 2006-2007, Ext JS, LLC.
36027  *
36028  * Originally Released Under LGPL - original licence link has changed is not relivant.
36029  *
36030  * Fork - LGPL
36031  * <script type="text/javascript">
36032  */
36033  
36034
36035 /**
36036  * @class Roo.tree.DefaultSelectionModel
36037  * @extends Roo.util.Observable
36038  * The default single selection for a TreePanel.
36039  * @param {Object} cfg Configuration
36040  */
36041 Roo.tree.DefaultSelectionModel = function(cfg){
36042    this.selNode = null;
36043    
36044    
36045    
36046    this.addEvents({
36047        /**
36048         * @event selectionchange
36049         * Fires when the selected node changes
36050         * @param {DefaultSelectionModel} this
36051         * @param {TreeNode} node the new selection
36052         */
36053        "selectionchange" : true,
36054
36055        /**
36056         * @event beforeselect
36057         * Fires before the selected node changes, return false to cancel the change
36058         * @param {DefaultSelectionModel} this
36059         * @param {TreeNode} node the new selection
36060         * @param {TreeNode} node the old selection
36061         */
36062        "beforeselect" : true
36063    });
36064    
36065     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
36066 };
36067
36068 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
36069     init : function(tree){
36070         this.tree = tree;
36071         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36072         tree.on("click", this.onNodeClick, this);
36073     },
36074     
36075     onNodeClick : function(node, e){
36076         if (e.ctrlKey && this.selNode == node)  {
36077             this.unselect(node);
36078             return;
36079         }
36080         this.select(node);
36081     },
36082     
36083     /**
36084      * Select a node.
36085      * @param {TreeNode} node The node to select
36086      * @return {TreeNode} The selected node
36087      */
36088     select : function(node){
36089         var last = this.selNode;
36090         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
36091             if(last){
36092                 last.ui.onSelectedChange(false);
36093             }
36094             this.selNode = node;
36095             node.ui.onSelectedChange(true);
36096             this.fireEvent("selectionchange", this, node, last);
36097         }
36098         return node;
36099     },
36100     
36101     /**
36102      * Deselect a node.
36103      * @param {TreeNode} node The node to unselect
36104      */
36105     unselect : function(node){
36106         if(this.selNode == node){
36107             this.clearSelections();
36108         }    
36109     },
36110     
36111     /**
36112      * Clear all selections
36113      */
36114     clearSelections : function(){
36115         var n = this.selNode;
36116         if(n){
36117             n.ui.onSelectedChange(false);
36118             this.selNode = null;
36119             this.fireEvent("selectionchange", this, null);
36120         }
36121         return n;
36122     },
36123     
36124     /**
36125      * Get the selected node
36126      * @return {TreeNode} The selected node
36127      */
36128     getSelectedNode : function(){
36129         return this.selNode;    
36130     },
36131     
36132     /**
36133      * Returns true if the node is selected
36134      * @param {TreeNode} node The node to check
36135      * @return {Boolean}
36136      */
36137     isSelected : function(node){
36138         return this.selNode == node;  
36139     },
36140
36141     /**
36142      * Selects the node above the selected node in the tree, intelligently walking the nodes
36143      * @return TreeNode The new selection
36144      */
36145     selectPrevious : function(){
36146         var s = this.selNode || this.lastSelNode;
36147         if(!s){
36148             return null;
36149         }
36150         var ps = s.previousSibling;
36151         if(ps){
36152             if(!ps.isExpanded() || ps.childNodes.length < 1){
36153                 return this.select(ps);
36154             } else{
36155                 var lc = ps.lastChild;
36156                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
36157                     lc = lc.lastChild;
36158                 }
36159                 return this.select(lc);
36160             }
36161         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
36162             return this.select(s.parentNode);
36163         }
36164         return null;
36165     },
36166
36167     /**
36168      * Selects the node above the selected node in the tree, intelligently walking the nodes
36169      * @return TreeNode The new selection
36170      */
36171     selectNext : function(){
36172         var s = this.selNode || this.lastSelNode;
36173         if(!s){
36174             return null;
36175         }
36176         if(s.firstChild && s.isExpanded()){
36177              return this.select(s.firstChild);
36178          }else if(s.nextSibling){
36179              return this.select(s.nextSibling);
36180          }else if(s.parentNode){
36181             var newS = null;
36182             s.parentNode.bubble(function(){
36183                 if(this.nextSibling){
36184                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
36185                     return false;
36186                 }
36187             });
36188             return newS;
36189          }
36190         return null;
36191     },
36192
36193     onKeyDown : function(e){
36194         var s = this.selNode || this.lastSelNode;
36195         // undesirable, but required
36196         var sm = this;
36197         if(!s){
36198             return;
36199         }
36200         var k = e.getKey();
36201         switch(k){
36202              case e.DOWN:
36203                  e.stopEvent();
36204                  this.selectNext();
36205              break;
36206              case e.UP:
36207                  e.stopEvent();
36208                  this.selectPrevious();
36209              break;
36210              case e.RIGHT:
36211                  e.preventDefault();
36212                  if(s.hasChildNodes()){
36213                      if(!s.isExpanded()){
36214                          s.expand();
36215                      }else if(s.firstChild){
36216                          this.select(s.firstChild, e);
36217                      }
36218                  }
36219              break;
36220              case e.LEFT:
36221                  e.preventDefault();
36222                  if(s.hasChildNodes() && s.isExpanded()){
36223                      s.collapse();
36224                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
36225                      this.select(s.parentNode, e);
36226                  }
36227              break;
36228         };
36229     }
36230 });
36231
36232 /**
36233  * @class Roo.tree.MultiSelectionModel
36234  * @extends Roo.util.Observable
36235  * Multi selection for a TreePanel.
36236  * @param {Object} cfg Configuration
36237  */
36238 Roo.tree.MultiSelectionModel = function(){
36239    this.selNodes = [];
36240    this.selMap = {};
36241    this.addEvents({
36242        /**
36243         * @event selectionchange
36244         * Fires when the selected nodes change
36245         * @param {MultiSelectionModel} this
36246         * @param {Array} nodes Array of the selected nodes
36247         */
36248        "selectionchange" : true
36249    });
36250    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
36251    
36252 };
36253
36254 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
36255     init : function(tree){
36256         this.tree = tree;
36257         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36258         tree.on("click", this.onNodeClick, this);
36259     },
36260     
36261     onNodeClick : function(node, e){
36262         this.select(node, e, e.ctrlKey);
36263     },
36264     
36265     /**
36266      * Select a node.
36267      * @param {TreeNode} node The node to select
36268      * @param {EventObject} e (optional) An event associated with the selection
36269      * @param {Boolean} keepExisting True to retain existing selections
36270      * @return {TreeNode} The selected node
36271      */
36272     select : function(node, e, keepExisting){
36273         if(keepExisting !== true){
36274             this.clearSelections(true);
36275         }
36276         if(this.isSelected(node)){
36277             this.lastSelNode = node;
36278             return node;
36279         }
36280         this.selNodes.push(node);
36281         this.selMap[node.id] = node;
36282         this.lastSelNode = node;
36283         node.ui.onSelectedChange(true);
36284         this.fireEvent("selectionchange", this, this.selNodes);
36285         return node;
36286     },
36287     
36288     /**
36289      * Deselect a node.
36290      * @param {TreeNode} node The node to unselect
36291      */
36292     unselect : function(node){
36293         if(this.selMap[node.id]){
36294             node.ui.onSelectedChange(false);
36295             var sn = this.selNodes;
36296             var index = -1;
36297             if(sn.indexOf){
36298                 index = sn.indexOf(node);
36299             }else{
36300                 for(var i = 0, len = sn.length; i < len; i++){
36301                     if(sn[i] == node){
36302                         index = i;
36303                         break;
36304                     }
36305                 }
36306             }
36307             if(index != -1){
36308                 this.selNodes.splice(index, 1);
36309             }
36310             delete this.selMap[node.id];
36311             this.fireEvent("selectionchange", this, this.selNodes);
36312         }
36313     },
36314     
36315     /**
36316      * Clear all selections
36317      */
36318     clearSelections : function(suppressEvent){
36319         var sn = this.selNodes;
36320         if(sn.length > 0){
36321             for(var i = 0, len = sn.length; i < len; i++){
36322                 sn[i].ui.onSelectedChange(false);
36323             }
36324             this.selNodes = [];
36325             this.selMap = {};
36326             if(suppressEvent !== true){
36327                 this.fireEvent("selectionchange", this, this.selNodes);
36328             }
36329         }
36330     },
36331     
36332     /**
36333      * Returns true if the node is selected
36334      * @param {TreeNode} node The node to check
36335      * @return {Boolean}
36336      */
36337     isSelected : function(node){
36338         return this.selMap[node.id] ? true : false;  
36339     },
36340     
36341     /**
36342      * Returns an array of the selected nodes
36343      * @return {Array}
36344      */
36345     getSelectedNodes : function(){
36346         return this.selNodes;    
36347     },
36348
36349     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36350
36351     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36352
36353     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36354 });/*
36355  * Based on:
36356  * Ext JS Library 1.1.1
36357  * Copyright(c) 2006-2007, Ext JS, LLC.
36358  *
36359  * Originally Released Under LGPL - original licence link has changed is not relivant.
36360  *
36361  * Fork - LGPL
36362  * <script type="text/javascript">
36363  */
36364  
36365 /**
36366  * @class Roo.tree.TreeNode
36367  * @extends Roo.data.Node
36368  * @cfg {String} text The text for this node
36369  * @cfg {Boolean} expanded true to start the node expanded
36370  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36371  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36372  * @cfg {Boolean} disabled true to start the node disabled
36373  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36374  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36375  * @cfg {String} cls A css class to be added to the node
36376  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36377  * @cfg {String} href URL of the link used for the node (defaults to #)
36378  * @cfg {String} hrefTarget target frame for the link
36379  * @cfg {String} qtip An Ext QuickTip for the node
36380  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36381  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36382  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36383  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36384  * (defaults to undefined with no checkbox rendered)
36385  * @constructor
36386  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36387  */
36388 Roo.tree.TreeNode = function(attributes){
36389     attributes = attributes || {};
36390     if(typeof attributes == "string"){
36391         attributes = {text: attributes};
36392     }
36393     this.childrenRendered = false;
36394     this.rendered = false;
36395     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36396     this.expanded = attributes.expanded === true;
36397     this.isTarget = attributes.isTarget !== false;
36398     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36399     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36400
36401     /**
36402      * Read-only. The text for this node. To change it use setText().
36403      * @type String
36404      */
36405     this.text = attributes.text;
36406     /**
36407      * True if this node is disabled.
36408      * @type Boolean
36409      */
36410     this.disabled = attributes.disabled === true;
36411
36412     this.addEvents({
36413         /**
36414         * @event textchange
36415         * Fires when the text for this node is changed
36416         * @param {Node} this This node
36417         * @param {String} text The new text
36418         * @param {String} oldText The old text
36419         */
36420         "textchange" : true,
36421         /**
36422         * @event beforeexpand
36423         * Fires before this node is expanded, return false to cancel.
36424         * @param {Node} this This node
36425         * @param {Boolean} deep
36426         * @param {Boolean} anim
36427         */
36428         "beforeexpand" : true,
36429         /**
36430         * @event beforecollapse
36431         * Fires before this node is collapsed, return false to cancel.
36432         * @param {Node} this This node
36433         * @param {Boolean} deep
36434         * @param {Boolean} anim
36435         */
36436         "beforecollapse" : true,
36437         /**
36438         * @event expand
36439         * Fires when this node is expanded
36440         * @param {Node} this This node
36441         */
36442         "expand" : true,
36443         /**
36444         * @event disabledchange
36445         * Fires when the disabled status of this node changes
36446         * @param {Node} this This node
36447         * @param {Boolean} disabled
36448         */
36449         "disabledchange" : true,
36450         /**
36451         * @event collapse
36452         * Fires when this node is collapsed
36453         * @param {Node} this This node
36454         */
36455         "collapse" : true,
36456         /**
36457         * @event beforeclick
36458         * Fires before click processing. Return false to cancel the default action.
36459         * @param {Node} this This node
36460         * @param {Roo.EventObject} e The event object
36461         */
36462         "beforeclick":true,
36463         /**
36464         * @event checkchange
36465         * Fires when a node with a checkbox's checked property changes
36466         * @param {Node} this This node
36467         * @param {Boolean} checked
36468         */
36469         "checkchange":true,
36470         /**
36471         * @event click
36472         * Fires when this node is clicked
36473         * @param {Node} this This node
36474         * @param {Roo.EventObject} e The event object
36475         */
36476         "click":true,
36477         /**
36478         * @event dblclick
36479         * Fires when this node is double clicked
36480         * @param {Node} this This node
36481         * @param {Roo.EventObject} e The event object
36482         */
36483         "dblclick":true,
36484         /**
36485         * @event contextmenu
36486         * Fires when this node is right clicked
36487         * @param {Node} this This node
36488         * @param {Roo.EventObject} e The event object
36489         */
36490         "contextmenu":true,
36491         /**
36492         * @event beforechildrenrendered
36493         * Fires right before the child nodes for this node are rendered
36494         * @param {Node} this This node
36495         */
36496         "beforechildrenrendered":true
36497     });
36498
36499     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36500
36501     /**
36502      * Read-only. The UI for this node
36503      * @type TreeNodeUI
36504      */
36505     this.ui = new uiClass(this);
36506     
36507     // finally support items[]
36508     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36509         return;
36510     }
36511     
36512     
36513     Roo.each(this.attributes.items, function(c) {
36514         this.appendChild(Roo.factory(c,Roo.Tree));
36515     }, this);
36516     delete this.attributes.items;
36517     
36518     
36519     
36520 };
36521 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36522     preventHScroll: true,
36523     /**
36524      * Returns true if this node is expanded
36525      * @return {Boolean}
36526      */
36527     isExpanded : function(){
36528         return this.expanded;
36529     },
36530
36531     /**
36532      * Returns the UI object for this node
36533      * @return {TreeNodeUI}
36534      */
36535     getUI : function(){
36536         return this.ui;
36537     },
36538
36539     // private override
36540     setFirstChild : function(node){
36541         var of = this.firstChild;
36542         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36543         if(this.childrenRendered && of && node != of){
36544             of.renderIndent(true, true);
36545         }
36546         if(this.rendered){
36547             this.renderIndent(true, true);
36548         }
36549     },
36550
36551     // private override
36552     setLastChild : function(node){
36553         var ol = this.lastChild;
36554         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36555         if(this.childrenRendered && ol && node != ol){
36556             ol.renderIndent(true, true);
36557         }
36558         if(this.rendered){
36559             this.renderIndent(true, true);
36560         }
36561     },
36562
36563     // these methods are overridden to provide lazy rendering support
36564     // private override
36565     appendChild : function()
36566     {
36567         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36568         if(node && this.childrenRendered){
36569             node.render();
36570         }
36571         this.ui.updateExpandIcon();
36572         return node;
36573     },
36574
36575     // private override
36576     removeChild : function(node){
36577         this.ownerTree.getSelectionModel().unselect(node);
36578         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36579         // if it's been rendered remove dom node
36580         if(this.childrenRendered){
36581             node.ui.remove();
36582         }
36583         if(this.childNodes.length < 1){
36584             this.collapse(false, false);
36585         }else{
36586             this.ui.updateExpandIcon();
36587         }
36588         if(!this.firstChild) {
36589             this.childrenRendered = false;
36590         }
36591         return node;
36592     },
36593
36594     // private override
36595     insertBefore : function(node, refNode){
36596         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36597         if(newNode && refNode && this.childrenRendered){
36598             node.render();
36599         }
36600         this.ui.updateExpandIcon();
36601         return newNode;
36602     },
36603
36604     /**
36605      * Sets the text for this node
36606      * @param {String} text
36607      */
36608     setText : function(text){
36609         var oldText = this.text;
36610         this.text = text;
36611         this.attributes.text = text;
36612         if(this.rendered){ // event without subscribing
36613             this.ui.onTextChange(this, text, oldText);
36614         }
36615         this.fireEvent("textchange", this, text, oldText);
36616     },
36617
36618     /**
36619      * Triggers selection of this node
36620      */
36621     select : function(){
36622         this.getOwnerTree().getSelectionModel().select(this);
36623     },
36624
36625     /**
36626      * Triggers deselection of this node
36627      */
36628     unselect : function(){
36629         this.getOwnerTree().getSelectionModel().unselect(this);
36630     },
36631
36632     /**
36633      * Returns true if this node is selected
36634      * @return {Boolean}
36635      */
36636     isSelected : function(){
36637         return this.getOwnerTree().getSelectionModel().isSelected(this);
36638     },
36639
36640     /**
36641      * Expand this node.
36642      * @param {Boolean} deep (optional) True to expand all children as well
36643      * @param {Boolean} anim (optional) false to cancel the default animation
36644      * @param {Function} callback (optional) A callback to be called when
36645      * expanding this node completes (does not wait for deep expand to complete).
36646      * Called with 1 parameter, this node.
36647      */
36648     expand : function(deep, anim, callback){
36649         if(!this.expanded){
36650             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36651                 return;
36652             }
36653             if(!this.childrenRendered){
36654                 this.renderChildren();
36655             }
36656             this.expanded = true;
36657             
36658             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36659                 this.ui.animExpand(function(){
36660                     this.fireEvent("expand", this);
36661                     if(typeof callback == "function"){
36662                         callback(this);
36663                     }
36664                     if(deep === true){
36665                         this.expandChildNodes(true);
36666                     }
36667                 }.createDelegate(this));
36668                 return;
36669             }else{
36670                 this.ui.expand();
36671                 this.fireEvent("expand", this);
36672                 if(typeof callback == "function"){
36673                     callback(this);
36674                 }
36675             }
36676         }else{
36677            if(typeof callback == "function"){
36678                callback(this);
36679            }
36680         }
36681         if(deep === true){
36682             this.expandChildNodes(true);
36683         }
36684     },
36685
36686     isHiddenRoot : function(){
36687         return this.isRoot && !this.getOwnerTree().rootVisible;
36688     },
36689
36690     /**
36691      * Collapse this node.
36692      * @param {Boolean} deep (optional) True to collapse all children as well
36693      * @param {Boolean} anim (optional) false to cancel the default animation
36694      */
36695     collapse : function(deep, anim){
36696         if(this.expanded && !this.isHiddenRoot()){
36697             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36698                 return;
36699             }
36700             this.expanded = false;
36701             if((this.getOwnerTree().animate && anim !== false) || anim){
36702                 this.ui.animCollapse(function(){
36703                     this.fireEvent("collapse", this);
36704                     if(deep === true){
36705                         this.collapseChildNodes(true);
36706                     }
36707                 }.createDelegate(this));
36708                 return;
36709             }else{
36710                 this.ui.collapse();
36711                 this.fireEvent("collapse", this);
36712             }
36713         }
36714         if(deep === true){
36715             var cs = this.childNodes;
36716             for(var i = 0, len = cs.length; i < len; i++) {
36717                 cs[i].collapse(true, false);
36718             }
36719         }
36720     },
36721
36722     // private
36723     delayedExpand : function(delay){
36724         if(!this.expandProcId){
36725             this.expandProcId = this.expand.defer(delay, this);
36726         }
36727     },
36728
36729     // private
36730     cancelExpand : function(){
36731         if(this.expandProcId){
36732             clearTimeout(this.expandProcId);
36733         }
36734         this.expandProcId = false;
36735     },
36736
36737     /**
36738      * Toggles expanded/collapsed state of the node
36739      */
36740     toggle : function(){
36741         if(this.expanded){
36742             this.collapse();
36743         }else{
36744             this.expand();
36745         }
36746     },
36747
36748     /**
36749      * Ensures all parent nodes are expanded
36750      */
36751     ensureVisible : function(callback){
36752         var tree = this.getOwnerTree();
36753         tree.expandPath(this.parentNode.getPath(), false, function(){
36754             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36755             Roo.callback(callback);
36756         }.createDelegate(this));
36757     },
36758
36759     /**
36760      * Expand all child nodes
36761      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36762      */
36763     expandChildNodes : function(deep){
36764         var cs = this.childNodes;
36765         for(var i = 0, len = cs.length; i < len; i++) {
36766                 cs[i].expand(deep);
36767         }
36768     },
36769
36770     /**
36771      * Collapse all child nodes
36772      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36773      */
36774     collapseChildNodes : function(deep){
36775         var cs = this.childNodes;
36776         for(var i = 0, len = cs.length; i < len; i++) {
36777                 cs[i].collapse(deep);
36778         }
36779     },
36780
36781     /**
36782      * Disables this node
36783      */
36784     disable : function(){
36785         this.disabled = true;
36786         this.unselect();
36787         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36788             this.ui.onDisableChange(this, true);
36789         }
36790         this.fireEvent("disabledchange", this, true);
36791     },
36792
36793     /**
36794      * Enables this node
36795      */
36796     enable : function(){
36797         this.disabled = false;
36798         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36799             this.ui.onDisableChange(this, false);
36800         }
36801         this.fireEvent("disabledchange", this, false);
36802     },
36803
36804     // private
36805     renderChildren : function(suppressEvent){
36806         if(suppressEvent !== false){
36807             this.fireEvent("beforechildrenrendered", this);
36808         }
36809         var cs = this.childNodes;
36810         for(var i = 0, len = cs.length; i < len; i++){
36811             cs[i].render(true);
36812         }
36813         this.childrenRendered = true;
36814     },
36815
36816     // private
36817     sort : function(fn, scope){
36818         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36819         if(this.childrenRendered){
36820             var cs = this.childNodes;
36821             for(var i = 0, len = cs.length; i < len; i++){
36822                 cs[i].render(true);
36823             }
36824         }
36825     },
36826
36827     // private
36828     render : function(bulkRender){
36829         this.ui.render(bulkRender);
36830         if(!this.rendered){
36831             this.rendered = true;
36832             if(this.expanded){
36833                 this.expanded = false;
36834                 this.expand(false, false);
36835             }
36836         }
36837     },
36838
36839     // private
36840     renderIndent : function(deep, refresh){
36841         if(refresh){
36842             this.ui.childIndent = null;
36843         }
36844         this.ui.renderIndent();
36845         if(deep === true && this.childrenRendered){
36846             var cs = this.childNodes;
36847             for(var i = 0, len = cs.length; i < len; i++){
36848                 cs[i].renderIndent(true, refresh);
36849             }
36850         }
36851     }
36852 });/*
36853  * Based on:
36854  * Ext JS Library 1.1.1
36855  * Copyright(c) 2006-2007, Ext JS, LLC.
36856  *
36857  * Originally Released Under LGPL - original licence link has changed is not relivant.
36858  *
36859  * Fork - LGPL
36860  * <script type="text/javascript">
36861  */
36862  
36863 /**
36864  * @class Roo.tree.AsyncTreeNode
36865  * @extends Roo.tree.TreeNode
36866  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36867  * @constructor
36868  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36869  */
36870  Roo.tree.AsyncTreeNode = function(config){
36871     this.loaded = false;
36872     this.loading = false;
36873     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36874     /**
36875     * @event beforeload
36876     * Fires before this node is loaded, return false to cancel
36877     * @param {Node} this This node
36878     */
36879     this.addEvents({'beforeload':true, 'load': true});
36880     /**
36881     * @event load
36882     * Fires when this node is loaded
36883     * @param {Node} this This node
36884     */
36885     /**
36886      * The loader used by this node (defaults to using the tree's defined loader)
36887      * @type TreeLoader
36888      * @property loader
36889      */
36890 };
36891 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36892     expand : function(deep, anim, callback){
36893         if(this.loading){ // if an async load is already running, waiting til it's done
36894             var timer;
36895             var f = function(){
36896                 if(!this.loading){ // done loading
36897                     clearInterval(timer);
36898                     this.expand(deep, anim, callback);
36899                 }
36900             }.createDelegate(this);
36901             timer = setInterval(f, 200);
36902             return;
36903         }
36904         if(!this.loaded){
36905             if(this.fireEvent("beforeload", this) === false){
36906                 return;
36907             }
36908             this.loading = true;
36909             this.ui.beforeLoad(this);
36910             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36911             if(loader){
36912                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36913                 return;
36914             }
36915         }
36916         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36917     },
36918     
36919     /**
36920      * Returns true if this node is currently loading
36921      * @return {Boolean}
36922      */
36923     isLoading : function(){
36924         return this.loading;  
36925     },
36926     
36927     loadComplete : function(deep, anim, callback){
36928         this.loading = false;
36929         this.loaded = true;
36930         this.ui.afterLoad(this);
36931         this.fireEvent("load", this);
36932         this.expand(deep, anim, callback);
36933     },
36934     
36935     /**
36936      * Returns true if this node has been loaded
36937      * @return {Boolean}
36938      */
36939     isLoaded : function(){
36940         return this.loaded;
36941     },
36942     
36943     hasChildNodes : function(){
36944         if(!this.isLeaf() && !this.loaded){
36945             return true;
36946         }else{
36947             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36948         }
36949     },
36950
36951     /**
36952      * Trigger a reload for this node
36953      * @param {Function} callback
36954      */
36955     reload : function(callback){
36956         this.collapse(false, false);
36957         while(this.firstChild){
36958             this.removeChild(this.firstChild);
36959         }
36960         this.childrenRendered = false;
36961         this.loaded = false;
36962         if(this.isHiddenRoot()){
36963             this.expanded = false;
36964         }
36965         this.expand(false, false, callback);
36966     }
36967 });/*
36968  * Based on:
36969  * Ext JS Library 1.1.1
36970  * Copyright(c) 2006-2007, Ext JS, LLC.
36971  *
36972  * Originally Released Under LGPL - original licence link has changed is not relivant.
36973  *
36974  * Fork - LGPL
36975  * <script type="text/javascript">
36976  */
36977  
36978 /**
36979  * @class Roo.tree.TreeNodeUI
36980  * @constructor
36981  * @param {Object} node The node to render
36982  * The TreeNode UI implementation is separate from the
36983  * tree implementation. Unless you are customizing the tree UI,
36984  * you should never have to use this directly.
36985  */
36986 Roo.tree.TreeNodeUI = function(node){
36987     this.node = node;
36988     this.rendered = false;
36989     this.animating = false;
36990     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36991 };
36992
36993 Roo.tree.TreeNodeUI.prototype = {
36994     removeChild : function(node){
36995         if(this.rendered){
36996             this.ctNode.removeChild(node.ui.getEl());
36997         }
36998     },
36999
37000     beforeLoad : function(){
37001          this.addClass("x-tree-node-loading");
37002     },
37003
37004     afterLoad : function(){
37005          this.removeClass("x-tree-node-loading");
37006     },
37007
37008     onTextChange : function(node, text, oldText){
37009         if(this.rendered){
37010             this.textNode.innerHTML = text;
37011         }
37012     },
37013
37014     onDisableChange : function(node, state){
37015         this.disabled = state;
37016         if(state){
37017             this.addClass("x-tree-node-disabled");
37018         }else{
37019             this.removeClass("x-tree-node-disabled");
37020         }
37021     },
37022
37023     onSelectedChange : function(state){
37024         if(state){
37025             this.focus();
37026             this.addClass("x-tree-selected");
37027         }else{
37028             //this.blur();
37029             this.removeClass("x-tree-selected");
37030         }
37031     },
37032
37033     onMove : function(tree, node, oldParent, newParent, index, refNode){
37034         this.childIndent = null;
37035         if(this.rendered){
37036             var targetNode = newParent.ui.getContainer();
37037             if(!targetNode){//target not rendered
37038                 this.holder = document.createElement("div");
37039                 this.holder.appendChild(this.wrap);
37040                 return;
37041             }
37042             var insertBefore = refNode ? refNode.ui.getEl() : null;
37043             if(insertBefore){
37044                 targetNode.insertBefore(this.wrap, insertBefore);
37045             }else{
37046                 targetNode.appendChild(this.wrap);
37047             }
37048             this.node.renderIndent(true);
37049         }
37050     },
37051
37052     addClass : function(cls){
37053         if(this.elNode){
37054             Roo.fly(this.elNode).addClass(cls);
37055         }
37056     },
37057
37058     removeClass : function(cls){
37059         if(this.elNode){
37060             Roo.fly(this.elNode).removeClass(cls);
37061         }
37062     },
37063
37064     remove : function(){
37065         if(this.rendered){
37066             this.holder = document.createElement("div");
37067             this.holder.appendChild(this.wrap);
37068         }
37069     },
37070
37071     fireEvent : function(){
37072         return this.node.fireEvent.apply(this.node, arguments);
37073     },
37074
37075     initEvents : function(){
37076         this.node.on("move", this.onMove, this);
37077         var E = Roo.EventManager;
37078         var a = this.anchor;
37079
37080         var el = Roo.fly(a, '_treeui');
37081
37082         if(Roo.isOpera){ // opera render bug ignores the CSS
37083             el.setStyle("text-decoration", "none");
37084         }
37085
37086         el.on("click", this.onClick, this);
37087         el.on("dblclick", this.onDblClick, this);
37088
37089         if(this.checkbox){
37090             Roo.EventManager.on(this.checkbox,
37091                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
37092         }
37093
37094         el.on("contextmenu", this.onContextMenu, this);
37095
37096         var icon = Roo.fly(this.iconNode);
37097         icon.on("click", this.onClick, this);
37098         icon.on("dblclick", this.onDblClick, this);
37099         icon.on("contextmenu", this.onContextMenu, this);
37100         E.on(this.ecNode, "click", this.ecClick, this, true);
37101
37102         if(this.node.disabled){
37103             this.addClass("x-tree-node-disabled");
37104         }
37105         if(this.node.hidden){
37106             this.addClass("x-tree-node-disabled");
37107         }
37108         var ot = this.node.getOwnerTree();
37109         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
37110         if(dd && (!this.node.isRoot || ot.rootVisible)){
37111             Roo.dd.Registry.register(this.elNode, {
37112                 node: this.node,
37113                 handles: this.getDDHandles(),
37114                 isHandle: false
37115             });
37116         }
37117     },
37118
37119     getDDHandles : function(){
37120         return [this.iconNode, this.textNode];
37121     },
37122
37123     hide : function(){
37124         if(this.rendered){
37125             this.wrap.style.display = "none";
37126         }
37127     },
37128
37129     show : function(){
37130         if(this.rendered){
37131             this.wrap.style.display = "";
37132         }
37133     },
37134
37135     onContextMenu : function(e){
37136         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
37137             e.preventDefault();
37138             this.focus();
37139             this.fireEvent("contextmenu", this.node, e);
37140         }
37141     },
37142
37143     onClick : function(e){
37144         if(this.dropping){
37145             e.stopEvent();
37146             return;
37147         }
37148         if(this.fireEvent("beforeclick", this.node, e) !== false){
37149             if(!this.disabled && this.node.attributes.href){
37150                 this.fireEvent("click", this.node, e);
37151                 return;
37152             }
37153             e.preventDefault();
37154             if(this.disabled){
37155                 return;
37156             }
37157
37158             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
37159                 this.node.toggle();
37160             }
37161
37162             this.fireEvent("click", this.node, e);
37163         }else{
37164             e.stopEvent();
37165         }
37166     },
37167
37168     onDblClick : function(e){
37169         e.preventDefault();
37170         if(this.disabled){
37171             return;
37172         }
37173         if(this.checkbox){
37174             this.toggleCheck();
37175         }
37176         if(!this.animating && this.node.hasChildNodes()){
37177             this.node.toggle();
37178         }
37179         this.fireEvent("dblclick", this.node, e);
37180     },
37181
37182     onCheckChange : function(){
37183         var checked = this.checkbox.checked;
37184         this.node.attributes.checked = checked;
37185         this.fireEvent('checkchange', this.node, checked);
37186     },
37187
37188     ecClick : function(e){
37189         if(!this.animating && this.node.hasChildNodes()){
37190             this.node.toggle();
37191         }
37192     },
37193
37194     startDrop : function(){
37195         this.dropping = true;
37196     },
37197
37198     // delayed drop so the click event doesn't get fired on a drop
37199     endDrop : function(){
37200        setTimeout(function(){
37201            this.dropping = false;
37202        }.createDelegate(this), 50);
37203     },
37204
37205     expand : function(){
37206         this.updateExpandIcon();
37207         this.ctNode.style.display = "";
37208     },
37209
37210     focus : function(){
37211         if(!this.node.preventHScroll){
37212             try{this.anchor.focus();
37213             }catch(e){}
37214         }else if(!Roo.isIE){
37215             try{
37216                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
37217                 var l = noscroll.scrollLeft;
37218                 this.anchor.focus();
37219                 noscroll.scrollLeft = l;
37220             }catch(e){}
37221         }
37222     },
37223
37224     toggleCheck : function(value){
37225         var cb = this.checkbox;
37226         if(cb){
37227             cb.checked = (value === undefined ? !cb.checked : value);
37228         }
37229     },
37230
37231     blur : function(){
37232         try{
37233             this.anchor.blur();
37234         }catch(e){}
37235     },
37236
37237     animExpand : function(callback){
37238         var ct = Roo.get(this.ctNode);
37239         ct.stopFx();
37240         if(!this.node.hasChildNodes()){
37241             this.updateExpandIcon();
37242             this.ctNode.style.display = "";
37243             Roo.callback(callback);
37244             return;
37245         }
37246         this.animating = true;
37247         this.updateExpandIcon();
37248
37249         ct.slideIn('t', {
37250            callback : function(){
37251                this.animating = false;
37252                Roo.callback(callback);
37253             },
37254             scope: this,
37255             duration: this.node.ownerTree.duration || .25
37256         });
37257     },
37258
37259     highlight : function(){
37260         var tree = this.node.getOwnerTree();
37261         Roo.fly(this.wrap).highlight(
37262             tree.hlColor || "C3DAF9",
37263             {endColor: tree.hlBaseColor}
37264         );
37265     },
37266
37267     collapse : function(){
37268         this.updateExpandIcon();
37269         this.ctNode.style.display = "none";
37270     },
37271
37272     animCollapse : function(callback){
37273         var ct = Roo.get(this.ctNode);
37274         ct.enableDisplayMode('block');
37275         ct.stopFx();
37276
37277         this.animating = true;
37278         this.updateExpandIcon();
37279
37280         ct.slideOut('t', {
37281             callback : function(){
37282                this.animating = false;
37283                Roo.callback(callback);
37284             },
37285             scope: this,
37286             duration: this.node.ownerTree.duration || .25
37287         });
37288     },
37289
37290     getContainer : function(){
37291         return this.ctNode;
37292     },
37293
37294     getEl : function(){
37295         return this.wrap;
37296     },
37297
37298     appendDDGhost : function(ghostNode){
37299         ghostNode.appendChild(this.elNode.cloneNode(true));
37300     },
37301
37302     getDDRepairXY : function(){
37303         return Roo.lib.Dom.getXY(this.iconNode);
37304     },
37305
37306     onRender : function(){
37307         this.render();
37308     },
37309
37310     render : function(bulkRender){
37311         var n = this.node, a = n.attributes;
37312         var targetNode = n.parentNode ?
37313               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
37314
37315         if(!this.rendered){
37316             this.rendered = true;
37317
37318             this.renderElements(n, a, targetNode, bulkRender);
37319
37320             if(a.qtip){
37321                if(this.textNode.setAttributeNS){
37322                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
37323                    if(a.qtipTitle){
37324                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
37325                    }
37326                }else{
37327                    this.textNode.setAttribute("ext:qtip", a.qtip);
37328                    if(a.qtipTitle){
37329                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37330                    }
37331                }
37332             }else if(a.qtipCfg){
37333                 a.qtipCfg.target = Roo.id(this.textNode);
37334                 Roo.QuickTips.register(a.qtipCfg);
37335             }
37336             this.initEvents();
37337             if(!this.node.expanded){
37338                 this.updateExpandIcon();
37339             }
37340         }else{
37341             if(bulkRender === true) {
37342                 targetNode.appendChild(this.wrap);
37343             }
37344         }
37345     },
37346
37347     renderElements : function(n, a, targetNode, bulkRender)
37348     {
37349         // add some indent caching, this helps performance when rendering a large tree
37350         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37351         var t = n.getOwnerTree();
37352         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37353         if (typeof(n.attributes.html) != 'undefined') {
37354             txt = n.attributes.html;
37355         }
37356         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37357         var cb = typeof a.checked == 'boolean';
37358         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37359         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37360             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37361             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37362             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37363             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37364             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37365              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37366                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37367             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37368             "</li>"];
37369
37370         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37371             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37372                                 n.nextSibling.ui.getEl(), buf.join(""));
37373         }else{
37374             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37375         }
37376
37377         this.elNode = this.wrap.childNodes[0];
37378         this.ctNode = this.wrap.childNodes[1];
37379         var cs = this.elNode.childNodes;
37380         this.indentNode = cs[0];
37381         this.ecNode = cs[1];
37382         this.iconNode = cs[2];
37383         var index = 3;
37384         if(cb){
37385             this.checkbox = cs[3];
37386             index++;
37387         }
37388         this.anchor = cs[index];
37389         this.textNode = cs[index].firstChild;
37390     },
37391
37392     getAnchor : function(){
37393         return this.anchor;
37394     },
37395
37396     getTextEl : function(){
37397         return this.textNode;
37398     },
37399
37400     getIconEl : function(){
37401         return this.iconNode;
37402     },
37403
37404     isChecked : function(){
37405         return this.checkbox ? this.checkbox.checked : false;
37406     },
37407
37408     updateExpandIcon : function(){
37409         if(this.rendered){
37410             var n = this.node, c1, c2;
37411             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37412             var hasChild = n.hasChildNodes();
37413             if(hasChild){
37414                 if(n.expanded){
37415                     cls += "-minus";
37416                     c1 = "x-tree-node-collapsed";
37417                     c2 = "x-tree-node-expanded";
37418                 }else{
37419                     cls += "-plus";
37420                     c1 = "x-tree-node-expanded";
37421                     c2 = "x-tree-node-collapsed";
37422                 }
37423                 if(this.wasLeaf){
37424                     this.removeClass("x-tree-node-leaf");
37425                     this.wasLeaf = false;
37426                 }
37427                 if(this.c1 != c1 || this.c2 != c2){
37428                     Roo.fly(this.elNode).replaceClass(c1, c2);
37429                     this.c1 = c1; this.c2 = c2;
37430                 }
37431             }else{
37432                 // this changes non-leafs into leafs if they have no children.
37433                 // it's not very rational behaviour..
37434                 
37435                 if(!this.wasLeaf && this.node.leaf){
37436                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37437                     delete this.c1;
37438                     delete this.c2;
37439                     this.wasLeaf = true;
37440                 }
37441             }
37442             var ecc = "x-tree-ec-icon "+cls;
37443             if(this.ecc != ecc){
37444                 this.ecNode.className = ecc;
37445                 this.ecc = ecc;
37446             }
37447         }
37448     },
37449
37450     getChildIndent : function(){
37451         if(!this.childIndent){
37452             var buf = [];
37453             var p = this.node;
37454             while(p){
37455                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37456                     if(!p.isLast()) {
37457                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37458                     } else {
37459                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37460                     }
37461                 }
37462                 p = p.parentNode;
37463             }
37464             this.childIndent = buf.join("");
37465         }
37466         return this.childIndent;
37467     },
37468
37469     renderIndent : function(){
37470         if(this.rendered){
37471             var indent = "";
37472             var p = this.node.parentNode;
37473             if(p){
37474                 indent = p.ui.getChildIndent();
37475             }
37476             if(this.indentMarkup != indent){ // don't rerender if not required
37477                 this.indentNode.innerHTML = indent;
37478                 this.indentMarkup = indent;
37479             }
37480             this.updateExpandIcon();
37481         }
37482     }
37483 };
37484
37485 Roo.tree.RootTreeNodeUI = function(){
37486     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37487 };
37488 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37489     render : function(){
37490         if(!this.rendered){
37491             var targetNode = this.node.ownerTree.innerCt.dom;
37492             this.node.expanded = true;
37493             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37494             this.wrap = this.ctNode = targetNode.firstChild;
37495         }
37496     },
37497     collapse : function(){
37498     },
37499     expand : function(){
37500     }
37501 });/*
37502  * Based on:
37503  * Ext JS Library 1.1.1
37504  * Copyright(c) 2006-2007, Ext JS, LLC.
37505  *
37506  * Originally Released Under LGPL - original licence link has changed is not relivant.
37507  *
37508  * Fork - LGPL
37509  * <script type="text/javascript">
37510  */
37511 /**
37512  * @class Roo.tree.TreeLoader
37513  * @extends Roo.util.Observable
37514  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37515  * nodes from a specified URL. The response must be a javascript Array definition
37516  * who's elements are node definition objects. eg:
37517  * <pre><code>
37518 {  success : true,
37519    data :      [
37520    
37521     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37522     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37523     ]
37524 }
37525
37526
37527 </code></pre>
37528  * <br><br>
37529  * The old style respose with just an array is still supported, but not recommended.
37530  * <br><br>
37531  *
37532  * A server request is sent, and child nodes are loaded only when a node is expanded.
37533  * The loading node's id is passed to the server under the parameter name "node" to
37534  * enable the server to produce the correct child nodes.
37535  * <br><br>
37536  * To pass extra parameters, an event handler may be attached to the "beforeload"
37537  * event, and the parameters specified in the TreeLoader's baseParams property:
37538  * <pre><code>
37539     myTreeLoader.on("beforeload", function(treeLoader, node) {
37540         this.baseParams.category = node.attributes.category;
37541     }, this);
37542     
37543 </code></pre>
37544  *
37545  * This would pass an HTTP parameter called "category" to the server containing
37546  * the value of the Node's "category" attribute.
37547  * @constructor
37548  * Creates a new Treeloader.
37549  * @param {Object} config A config object containing config properties.
37550  */
37551 Roo.tree.TreeLoader = function(config){
37552     this.baseParams = {};
37553     this.requestMethod = "POST";
37554     Roo.apply(this, config);
37555
37556     this.addEvents({
37557     
37558         /**
37559          * @event beforeload
37560          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37561          * @param {Object} This TreeLoader object.
37562          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37563          * @param {Object} callback The callback function specified in the {@link #load} call.
37564          */
37565         beforeload : true,
37566         /**
37567          * @event load
37568          * Fires when the node has been successfuly loaded.
37569          * @param {Object} This TreeLoader object.
37570          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37571          * @param {Object} response The response object containing the data from the server.
37572          */
37573         load : true,
37574         /**
37575          * @event loadexception
37576          * Fires if the network request failed.
37577          * @param {Object} This TreeLoader object.
37578          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37579          * @param {Object} response The response object containing the data from the server.
37580          */
37581         loadexception : true,
37582         /**
37583          * @event create
37584          * Fires before a node is created, enabling you to return custom Node types 
37585          * @param {Object} This TreeLoader object.
37586          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37587          */
37588         create : true
37589     });
37590
37591     Roo.tree.TreeLoader.superclass.constructor.call(this);
37592 };
37593
37594 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37595     /**
37596     * @cfg {String} dataUrl The URL from which to request a Json string which
37597     * specifies an array of node definition object representing the child nodes
37598     * to be loaded.
37599     */
37600     /**
37601     * @cfg {String} requestMethod either GET or POST
37602     * defaults to POST (due to BC)
37603     * to be loaded.
37604     */
37605     /**
37606     * @cfg {Object} baseParams (optional) An object containing properties which
37607     * specify HTTP parameters to be passed to each request for child nodes.
37608     */
37609     /**
37610     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37611     * created by this loader. If the attributes sent by the server have an attribute in this object,
37612     * they take priority.
37613     */
37614     /**
37615     * @cfg {Object} uiProviders (optional) An object containing properties which
37616     * 
37617     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37618     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37619     * <i>uiProvider</i> attribute of a returned child node is a string rather
37620     * than a reference to a TreeNodeUI implementation, this that string value
37621     * is used as a property name in the uiProviders object. You can define the provider named
37622     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37623     */
37624     uiProviders : {},
37625
37626     /**
37627     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37628     * child nodes before loading.
37629     */
37630     clearOnLoad : true,
37631
37632     /**
37633     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37634     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37635     * Grid query { data : [ .....] }
37636     */
37637     
37638     root : false,
37639      /**
37640     * @cfg {String} queryParam (optional) 
37641     * Name of the query as it will be passed on the querystring (defaults to 'node')
37642     * eg. the request will be ?node=[id]
37643     */
37644     
37645     
37646     queryParam: false,
37647     
37648     /**
37649      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37650      * This is called automatically when a node is expanded, but may be used to reload
37651      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37652      * @param {Roo.tree.TreeNode} node
37653      * @param {Function} callback
37654      */
37655     load : function(node, callback){
37656         if(this.clearOnLoad){
37657             while(node.firstChild){
37658                 node.removeChild(node.firstChild);
37659             }
37660         }
37661         if(node.attributes.children){ // preloaded json children
37662             var cs = node.attributes.children;
37663             for(var i = 0, len = cs.length; i < len; i++){
37664                 node.appendChild(this.createNode(cs[i]));
37665             }
37666             if(typeof callback == "function"){
37667                 callback();
37668             }
37669         }else if(this.dataUrl){
37670             this.requestData(node, callback);
37671         }
37672     },
37673
37674     getParams: function(node){
37675         var buf = [], bp = this.baseParams;
37676         for(var key in bp){
37677             if(typeof bp[key] != "function"){
37678                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37679             }
37680         }
37681         var n = this.queryParam === false ? 'node' : this.queryParam;
37682         buf.push(n + "=", encodeURIComponent(node.id));
37683         return buf.join("");
37684     },
37685
37686     requestData : function(node, callback){
37687         if(this.fireEvent("beforeload", this, node, callback) !== false){
37688             this.transId = Roo.Ajax.request({
37689                 method:this.requestMethod,
37690                 url: this.dataUrl||this.url,
37691                 success: this.handleResponse,
37692                 failure: this.handleFailure,
37693                 scope: this,
37694                 argument: {callback: callback, node: node},
37695                 params: this.getParams(node)
37696             });
37697         }else{
37698             // if the load is cancelled, make sure we notify
37699             // the node that we are done
37700             if(typeof callback == "function"){
37701                 callback();
37702             }
37703         }
37704     },
37705
37706     isLoading : function(){
37707         return this.transId ? true : false;
37708     },
37709
37710     abort : function(){
37711         if(this.isLoading()){
37712             Roo.Ajax.abort(this.transId);
37713         }
37714     },
37715
37716     // private
37717     createNode : function(attr)
37718     {
37719         // apply baseAttrs, nice idea Corey!
37720         if(this.baseAttrs){
37721             Roo.applyIf(attr, this.baseAttrs);
37722         }
37723         if(this.applyLoader !== false){
37724             attr.loader = this;
37725         }
37726         // uiProvider = depreciated..
37727         
37728         if(typeof(attr.uiProvider) == 'string'){
37729            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37730                 /**  eval:var:attr */ eval(attr.uiProvider);
37731         }
37732         if(typeof(this.uiProviders['default']) != 'undefined') {
37733             attr.uiProvider = this.uiProviders['default'];
37734         }
37735         
37736         this.fireEvent('create', this, attr);
37737         
37738         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37739         return(attr.leaf ?
37740                         new Roo.tree.TreeNode(attr) :
37741                         new Roo.tree.AsyncTreeNode(attr));
37742     },
37743
37744     processResponse : function(response, node, callback)
37745     {
37746         var json = response.responseText;
37747         try {
37748             
37749             var o = Roo.decode(json);
37750             
37751             if (this.root === false && typeof(o.success) != undefined) {
37752                 this.root = 'data'; // the default behaviour for list like data..
37753                 }
37754                 
37755             if (this.root !== false &&  !o.success) {
37756                 // it's a failure condition.
37757                 var a = response.argument;
37758                 this.fireEvent("loadexception", this, a.node, response);
37759                 Roo.log("Load failed - should have a handler really");
37760                 return;
37761             }
37762             
37763             
37764             
37765             if (this.root !== false) {
37766                  o = o[this.root];
37767             }
37768             
37769             for(var i = 0, len = o.length; i < len; i++){
37770                 var n = this.createNode(o[i]);
37771                 if(n){
37772                     node.appendChild(n);
37773                 }
37774             }
37775             if(typeof callback == "function"){
37776                 callback(this, node);
37777             }
37778         }catch(e){
37779             this.handleFailure(response);
37780         }
37781     },
37782
37783     handleResponse : function(response){
37784         this.transId = false;
37785         var a = response.argument;
37786         this.processResponse(response, a.node, a.callback);
37787         this.fireEvent("load", this, a.node, response);
37788     },
37789
37790     handleFailure : function(response)
37791     {
37792         // should handle failure better..
37793         this.transId = false;
37794         var a = response.argument;
37795         this.fireEvent("loadexception", this, a.node, response);
37796         if(typeof a.callback == "function"){
37797             a.callback(this, a.node);
37798         }
37799     }
37800 });/*
37801  * Based on:
37802  * Ext JS Library 1.1.1
37803  * Copyright(c) 2006-2007, Ext JS, LLC.
37804  *
37805  * Originally Released Under LGPL - original licence link has changed is not relivant.
37806  *
37807  * Fork - LGPL
37808  * <script type="text/javascript">
37809  */
37810
37811 /**
37812 * @class Roo.tree.TreeFilter
37813 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37814 * @param {TreePanel} tree
37815 * @param {Object} config (optional)
37816  */
37817 Roo.tree.TreeFilter = function(tree, config){
37818     this.tree = tree;
37819     this.filtered = {};
37820     Roo.apply(this, config);
37821 };
37822
37823 Roo.tree.TreeFilter.prototype = {
37824     clearBlank:false,
37825     reverse:false,
37826     autoClear:false,
37827     remove:false,
37828
37829      /**
37830      * Filter the data by a specific attribute.
37831      * @param {String/RegExp} value Either string that the attribute value
37832      * should start with or a RegExp to test against the attribute
37833      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37834      * @param {TreeNode} startNode (optional) The node to start the filter at.
37835      */
37836     filter : function(value, attr, startNode){
37837         attr = attr || "text";
37838         var f;
37839         if(typeof value == "string"){
37840             var vlen = value.length;
37841             // auto clear empty filter
37842             if(vlen == 0 && this.clearBlank){
37843                 this.clear();
37844                 return;
37845             }
37846             value = value.toLowerCase();
37847             f = function(n){
37848                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37849             };
37850         }else if(value.exec){ // regex?
37851             f = function(n){
37852                 return value.test(n.attributes[attr]);
37853             };
37854         }else{
37855             throw 'Illegal filter type, must be string or regex';
37856         }
37857         this.filterBy(f, null, startNode);
37858         },
37859
37860     /**
37861      * Filter by a function. The passed function will be called with each
37862      * node in the tree (or from the startNode). If the function returns true, the node is kept
37863      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37864      * @param {Function} fn The filter function
37865      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37866      */
37867     filterBy : function(fn, scope, startNode){
37868         startNode = startNode || this.tree.root;
37869         if(this.autoClear){
37870             this.clear();
37871         }
37872         var af = this.filtered, rv = this.reverse;
37873         var f = function(n){
37874             if(n == startNode){
37875                 return true;
37876             }
37877             if(af[n.id]){
37878                 return false;
37879             }
37880             var m = fn.call(scope || n, n);
37881             if(!m || rv){
37882                 af[n.id] = n;
37883                 n.ui.hide();
37884                 return false;
37885             }
37886             return true;
37887         };
37888         startNode.cascade(f);
37889         if(this.remove){
37890            for(var id in af){
37891                if(typeof id != "function"){
37892                    var n = af[id];
37893                    if(n && n.parentNode){
37894                        n.parentNode.removeChild(n);
37895                    }
37896                }
37897            }
37898         }
37899     },
37900
37901     /**
37902      * Clears the current filter. Note: with the "remove" option
37903      * set a filter cannot be cleared.
37904      */
37905     clear : function(){
37906         var t = this.tree;
37907         var af = this.filtered;
37908         for(var id in af){
37909             if(typeof id != "function"){
37910                 var n = af[id];
37911                 if(n){
37912                     n.ui.show();
37913                 }
37914             }
37915         }
37916         this.filtered = {};
37917     }
37918 };
37919 /*
37920  * Based on:
37921  * Ext JS Library 1.1.1
37922  * Copyright(c) 2006-2007, Ext JS, LLC.
37923  *
37924  * Originally Released Under LGPL - original licence link has changed is not relivant.
37925  *
37926  * Fork - LGPL
37927  * <script type="text/javascript">
37928  */
37929  
37930
37931 /**
37932  * @class Roo.tree.TreeSorter
37933  * Provides sorting of nodes in a TreePanel
37934  * 
37935  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37936  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37937  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37938  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37939  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37940  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37941  * @constructor
37942  * @param {TreePanel} tree
37943  * @param {Object} config
37944  */
37945 Roo.tree.TreeSorter = function(tree, config){
37946     Roo.apply(this, config);
37947     tree.on("beforechildrenrendered", this.doSort, this);
37948     tree.on("append", this.updateSort, this);
37949     tree.on("insert", this.updateSort, this);
37950     
37951     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37952     var p = this.property || "text";
37953     var sortType = this.sortType;
37954     var fs = this.folderSort;
37955     var cs = this.caseSensitive === true;
37956     var leafAttr = this.leafAttr || 'leaf';
37957
37958     this.sortFn = function(n1, n2){
37959         if(fs){
37960             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37961                 return 1;
37962             }
37963             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37964                 return -1;
37965             }
37966         }
37967         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37968         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37969         if(v1 < v2){
37970                         return dsc ? +1 : -1;
37971                 }else if(v1 > v2){
37972                         return dsc ? -1 : +1;
37973         }else{
37974                 return 0;
37975         }
37976     };
37977 };
37978
37979 Roo.tree.TreeSorter.prototype = {
37980     doSort : function(node){
37981         node.sort(this.sortFn);
37982     },
37983     
37984     compareNodes : function(n1, n2){
37985         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37986     },
37987     
37988     updateSort : function(tree, node){
37989         if(node.childrenRendered){
37990             this.doSort.defer(1, this, [node]);
37991         }
37992     }
37993 };/*
37994  * Based on:
37995  * Ext JS Library 1.1.1
37996  * Copyright(c) 2006-2007, Ext JS, LLC.
37997  *
37998  * Originally Released Under LGPL - original licence link has changed is not relivant.
37999  *
38000  * Fork - LGPL
38001  * <script type="text/javascript">
38002  */
38003
38004 if(Roo.dd.DropZone){
38005     
38006 Roo.tree.TreeDropZone = function(tree, config){
38007     this.allowParentInsert = false;
38008     this.allowContainerDrop = false;
38009     this.appendOnly = false;
38010     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
38011     this.tree = tree;
38012     this.lastInsertClass = "x-tree-no-status";
38013     this.dragOverData = {};
38014 };
38015
38016 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
38017     ddGroup : "TreeDD",
38018     scroll:  true,
38019     
38020     expandDelay : 1000,
38021     
38022     expandNode : function(node){
38023         if(node.hasChildNodes() && !node.isExpanded()){
38024             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
38025         }
38026     },
38027     
38028     queueExpand : function(node){
38029         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
38030     },
38031     
38032     cancelExpand : function(){
38033         if(this.expandProcId){
38034             clearTimeout(this.expandProcId);
38035             this.expandProcId = false;
38036         }
38037     },
38038     
38039     isValidDropPoint : function(n, pt, dd, e, data){
38040         if(!n || !data){ return false; }
38041         var targetNode = n.node;
38042         var dropNode = data.node;
38043         // default drop rules
38044         if(!(targetNode && targetNode.isTarget && pt)){
38045             return false;
38046         }
38047         if(pt == "append" && targetNode.allowChildren === false){
38048             return false;
38049         }
38050         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
38051             return false;
38052         }
38053         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
38054             return false;
38055         }
38056         // reuse the object
38057         var overEvent = this.dragOverData;
38058         overEvent.tree = this.tree;
38059         overEvent.target = targetNode;
38060         overEvent.data = data;
38061         overEvent.point = pt;
38062         overEvent.source = dd;
38063         overEvent.rawEvent = e;
38064         overEvent.dropNode = dropNode;
38065         overEvent.cancel = false;  
38066         var result = this.tree.fireEvent("nodedragover", overEvent);
38067         return overEvent.cancel === false && result !== false;
38068     },
38069     
38070     getDropPoint : function(e, n, dd)
38071     {
38072         var tn = n.node;
38073         if(tn.isRoot){
38074             return tn.allowChildren !== false ? "append" : false; // always append for root
38075         }
38076         var dragEl = n.ddel;
38077         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
38078         var y = Roo.lib.Event.getPageY(e);
38079         //var noAppend = tn.allowChildren === false || tn.isLeaf();
38080         
38081         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
38082         var noAppend = tn.allowChildren === false;
38083         if(this.appendOnly || tn.parentNode.allowChildren === false){
38084             return noAppend ? false : "append";
38085         }
38086         var noBelow = false;
38087         if(!this.allowParentInsert){
38088             noBelow = tn.hasChildNodes() && tn.isExpanded();
38089         }
38090         var q = (b - t) / (noAppend ? 2 : 3);
38091         if(y >= t && y < (t + q)){
38092             return "above";
38093         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
38094             return "below";
38095         }else{
38096             return "append";
38097         }
38098     },
38099     
38100     onNodeEnter : function(n, dd, e, data)
38101     {
38102         this.cancelExpand();
38103     },
38104     
38105     onNodeOver : function(n, dd, e, data)
38106     {
38107        
38108         var pt = this.getDropPoint(e, n, dd);
38109         var node = n.node;
38110         
38111         // auto node expand check
38112         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
38113             this.queueExpand(node);
38114         }else if(pt != "append"){
38115             this.cancelExpand();
38116         }
38117         
38118         // set the insert point style on the target node
38119         var returnCls = this.dropNotAllowed;
38120         if(this.isValidDropPoint(n, pt, dd, e, data)){
38121            if(pt){
38122                var el = n.ddel;
38123                var cls;
38124                if(pt == "above"){
38125                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
38126                    cls = "x-tree-drag-insert-above";
38127                }else if(pt == "below"){
38128                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
38129                    cls = "x-tree-drag-insert-below";
38130                }else{
38131                    returnCls = "x-tree-drop-ok-append";
38132                    cls = "x-tree-drag-append";
38133                }
38134                if(this.lastInsertClass != cls){
38135                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
38136                    this.lastInsertClass = cls;
38137                }
38138            }
38139        }
38140        return returnCls;
38141     },
38142     
38143     onNodeOut : function(n, dd, e, data){
38144         
38145         this.cancelExpand();
38146         this.removeDropIndicators(n);
38147     },
38148     
38149     onNodeDrop : function(n, dd, e, data){
38150         var point = this.getDropPoint(e, n, dd);
38151         var targetNode = n.node;
38152         targetNode.ui.startDrop();
38153         if(!this.isValidDropPoint(n, point, dd, e, data)){
38154             targetNode.ui.endDrop();
38155             return false;
38156         }
38157         // first try to find the drop node
38158         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
38159         var dropEvent = {
38160             tree : this.tree,
38161             target: targetNode,
38162             data: data,
38163             point: point,
38164             source: dd,
38165             rawEvent: e,
38166             dropNode: dropNode,
38167             cancel: !dropNode   
38168         };
38169         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
38170         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
38171             targetNode.ui.endDrop();
38172             return false;
38173         }
38174         // allow target changing
38175         targetNode = dropEvent.target;
38176         if(point == "append" && !targetNode.isExpanded()){
38177             targetNode.expand(false, null, function(){
38178                 this.completeDrop(dropEvent);
38179             }.createDelegate(this));
38180         }else{
38181             this.completeDrop(dropEvent);
38182         }
38183         return true;
38184     },
38185     
38186     completeDrop : function(de){
38187         var ns = de.dropNode, p = de.point, t = de.target;
38188         if(!(ns instanceof Array)){
38189             ns = [ns];
38190         }
38191         var n;
38192         for(var i = 0, len = ns.length; i < len; i++){
38193             n = ns[i];
38194             if(p == "above"){
38195                 t.parentNode.insertBefore(n, t);
38196             }else if(p == "below"){
38197                 t.parentNode.insertBefore(n, t.nextSibling);
38198             }else{
38199                 t.appendChild(n);
38200             }
38201         }
38202         n.ui.focus();
38203         if(this.tree.hlDrop){
38204             n.ui.highlight();
38205         }
38206         t.ui.endDrop();
38207         this.tree.fireEvent("nodedrop", de);
38208     },
38209     
38210     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
38211         if(this.tree.hlDrop){
38212             dropNode.ui.focus();
38213             dropNode.ui.highlight();
38214         }
38215         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
38216     },
38217     
38218     getTree : function(){
38219         return this.tree;
38220     },
38221     
38222     removeDropIndicators : function(n){
38223         if(n && n.ddel){
38224             var el = n.ddel;
38225             Roo.fly(el).removeClass([
38226                     "x-tree-drag-insert-above",
38227                     "x-tree-drag-insert-below",
38228                     "x-tree-drag-append"]);
38229             this.lastInsertClass = "_noclass";
38230         }
38231     },
38232     
38233     beforeDragDrop : function(target, e, id){
38234         this.cancelExpand();
38235         return true;
38236     },
38237     
38238     afterRepair : function(data){
38239         if(data && Roo.enableFx){
38240             data.node.ui.highlight();
38241         }
38242         this.hideProxy();
38243     } 
38244     
38245 });
38246
38247 }
38248 /*
38249  * Based on:
38250  * Ext JS Library 1.1.1
38251  * Copyright(c) 2006-2007, Ext JS, LLC.
38252  *
38253  * Originally Released Under LGPL - original licence link has changed is not relivant.
38254  *
38255  * Fork - LGPL
38256  * <script type="text/javascript">
38257  */
38258  
38259
38260 if(Roo.dd.DragZone){
38261 Roo.tree.TreeDragZone = function(tree, config){
38262     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
38263     this.tree = tree;
38264 };
38265
38266 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
38267     ddGroup : "TreeDD",
38268    
38269     onBeforeDrag : function(data, e){
38270         var n = data.node;
38271         return n && n.draggable && !n.disabled;
38272     },
38273      
38274     
38275     onInitDrag : function(e){
38276         var data = this.dragData;
38277         this.tree.getSelectionModel().select(data.node);
38278         this.proxy.update("");
38279         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
38280         this.tree.fireEvent("startdrag", this.tree, data.node, e);
38281     },
38282     
38283     getRepairXY : function(e, data){
38284         return data.node.ui.getDDRepairXY();
38285     },
38286     
38287     onEndDrag : function(data, e){
38288         this.tree.fireEvent("enddrag", this.tree, data.node, e);
38289         
38290         
38291     },
38292     
38293     onValidDrop : function(dd, e, id){
38294         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
38295         this.hideProxy();
38296     },
38297     
38298     beforeInvalidDrop : function(e, id){
38299         // this scrolls the original position back into view
38300         var sm = this.tree.getSelectionModel();
38301         sm.clearSelections();
38302         sm.select(this.dragData.node);
38303     }
38304 });
38305 }/*
38306  * Based on:
38307  * Ext JS Library 1.1.1
38308  * Copyright(c) 2006-2007, Ext JS, LLC.
38309  *
38310  * Originally Released Under LGPL - original licence link has changed is not relivant.
38311  *
38312  * Fork - LGPL
38313  * <script type="text/javascript">
38314  */
38315 /**
38316  * @class Roo.tree.TreeEditor
38317  * @extends Roo.Editor
38318  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
38319  * as the editor field.
38320  * @constructor
38321  * @param {Object} config (used to be the tree panel.)
38322  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
38323  * 
38324  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
38325  * @cfg {Roo.form.TextField} field [required] The field configuration
38326  *
38327  * 
38328  */
38329 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38330     var tree = config;
38331     var field;
38332     if (oldconfig) { // old style..
38333         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38334     } else {
38335         // new style..
38336         tree = config.tree;
38337         config.field = config.field  || {};
38338         config.field.xtype = 'TextField';
38339         field = Roo.factory(config.field, Roo.form);
38340     }
38341     config = config || {};
38342     
38343     
38344     this.addEvents({
38345         /**
38346          * @event beforenodeedit
38347          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
38348          * false from the handler of this event.
38349          * @param {Editor} this
38350          * @param {Roo.tree.Node} node 
38351          */
38352         "beforenodeedit" : true
38353     });
38354     
38355     //Roo.log(config);
38356     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38357
38358     this.tree = tree;
38359
38360     tree.on('beforeclick', this.beforeNodeClick, this);
38361     tree.getTreeEl().on('mousedown', this.hide, this);
38362     this.on('complete', this.updateNode, this);
38363     this.on('beforestartedit', this.fitToTree, this);
38364     this.on('startedit', this.bindScroll, this, {delay:10});
38365     this.on('specialkey', this.onSpecialKey, this);
38366 };
38367
38368 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38369     /**
38370      * @cfg {String} alignment
38371      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38372      */
38373     alignment: "l-l",
38374     // inherit
38375     autoSize: false,
38376     /**
38377      * @cfg {Boolean} hideEl
38378      * True to hide the bound element while the editor is displayed (defaults to false)
38379      */
38380     hideEl : false,
38381     /**
38382      * @cfg {String} cls
38383      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38384      */
38385     cls: "x-small-editor x-tree-editor",
38386     /**
38387      * @cfg {Boolean} shim
38388      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38389      */
38390     shim:false,
38391     // inherit
38392     shadow:"frame",
38393     /**
38394      * @cfg {Number} maxWidth
38395      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38396      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38397      * scroll and client offsets into account prior to each edit.
38398      */
38399     maxWidth: 250,
38400
38401     editDelay : 350,
38402
38403     // private
38404     fitToTree : function(ed, el){
38405         var td = this.tree.getTreeEl().dom, nd = el.dom;
38406         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38407             td.scrollLeft = nd.offsetLeft;
38408         }
38409         var w = Math.min(
38410                 this.maxWidth,
38411                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38412         this.setSize(w, '');
38413         
38414         return this.fireEvent('beforenodeedit', this, this.editNode);
38415         
38416     },
38417
38418     // private
38419     triggerEdit : function(node){
38420         this.completeEdit();
38421         this.editNode = node;
38422         this.startEdit(node.ui.textNode, node.text);
38423     },
38424
38425     // private
38426     bindScroll : function(){
38427         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38428     },
38429
38430     // private
38431     beforeNodeClick : function(node, e){
38432         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38433         this.lastClick = new Date();
38434         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38435             e.stopEvent();
38436             this.triggerEdit(node);
38437             return false;
38438         }
38439         return true;
38440     },
38441
38442     // private
38443     updateNode : function(ed, value){
38444         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38445         this.editNode.setText(value);
38446     },
38447
38448     // private
38449     onHide : function(){
38450         Roo.tree.TreeEditor.superclass.onHide.call(this);
38451         if(this.editNode){
38452             this.editNode.ui.focus();
38453         }
38454     },
38455
38456     // private
38457     onSpecialKey : function(field, e){
38458         var k = e.getKey();
38459         if(k == e.ESC){
38460             e.stopEvent();
38461             this.cancelEdit();
38462         }else if(k == e.ENTER && !e.hasModifier()){
38463             e.stopEvent();
38464             this.completeEdit();
38465         }
38466     }
38467 });//<Script type="text/javascript">
38468 /*
38469  * Based on:
38470  * Ext JS Library 1.1.1
38471  * Copyright(c) 2006-2007, Ext JS, LLC.
38472  *
38473  * Originally Released Under LGPL - original licence link has changed is not relivant.
38474  *
38475  * Fork - LGPL
38476  * <script type="text/javascript">
38477  */
38478  
38479 /**
38480  * Not documented??? - probably should be...
38481  */
38482
38483 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38484     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38485     
38486     renderElements : function(n, a, targetNode, bulkRender){
38487         //consel.log("renderElements?");
38488         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38489
38490         var t = n.getOwnerTree();
38491         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38492         
38493         var cols = t.columns;
38494         var bw = t.borderWidth;
38495         var c = cols[0];
38496         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38497          var cb = typeof a.checked == "boolean";
38498         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38499         var colcls = 'x-t-' + tid + '-c0';
38500         var buf = [
38501             '<li class="x-tree-node">',
38502             
38503                 
38504                 '<div class="x-tree-node-el ', a.cls,'">',
38505                     // extran...
38506                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38507                 
38508                 
38509                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38510                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38511                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38512                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38513                            (a.iconCls ? ' '+a.iconCls : ''),
38514                            '" unselectable="on" />',
38515                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38516                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38517                              
38518                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38519                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38520                             '<span unselectable="on" qtip="' + tx + '">',
38521                              tx,
38522                              '</span></a>' ,
38523                     '</div>',
38524                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38525                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38526                  ];
38527         for(var i = 1, len = cols.length; i < len; i++){
38528             c = cols[i];
38529             colcls = 'x-t-' + tid + '-c' +i;
38530             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38531             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38532                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38533                       "</div>");
38534          }
38535          
38536          buf.push(
38537             '</a>',
38538             '<div class="x-clear"></div></div>',
38539             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38540             "</li>");
38541         
38542         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38543             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38544                                 n.nextSibling.ui.getEl(), buf.join(""));
38545         }else{
38546             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38547         }
38548         var el = this.wrap.firstChild;
38549         this.elRow = el;
38550         this.elNode = el.firstChild;
38551         this.ranchor = el.childNodes[1];
38552         this.ctNode = this.wrap.childNodes[1];
38553         var cs = el.firstChild.childNodes;
38554         this.indentNode = cs[0];
38555         this.ecNode = cs[1];
38556         this.iconNode = cs[2];
38557         var index = 3;
38558         if(cb){
38559             this.checkbox = cs[3];
38560             index++;
38561         }
38562         this.anchor = cs[index];
38563         
38564         this.textNode = cs[index].firstChild;
38565         
38566         //el.on("click", this.onClick, this);
38567         //el.on("dblclick", this.onDblClick, this);
38568         
38569         
38570        // console.log(this);
38571     },
38572     initEvents : function(){
38573         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38574         
38575             
38576         var a = this.ranchor;
38577
38578         var el = Roo.get(a);
38579
38580         if(Roo.isOpera){ // opera render bug ignores the CSS
38581             el.setStyle("text-decoration", "none");
38582         }
38583
38584         el.on("click", this.onClick, this);
38585         el.on("dblclick", this.onDblClick, this);
38586         el.on("contextmenu", this.onContextMenu, this);
38587         
38588     },
38589     
38590     /*onSelectedChange : function(state){
38591         if(state){
38592             this.focus();
38593             this.addClass("x-tree-selected");
38594         }else{
38595             //this.blur();
38596             this.removeClass("x-tree-selected");
38597         }
38598     },*/
38599     addClass : function(cls){
38600         if(this.elRow){
38601             Roo.fly(this.elRow).addClass(cls);
38602         }
38603         
38604     },
38605     
38606     
38607     removeClass : function(cls){
38608         if(this.elRow){
38609             Roo.fly(this.elRow).removeClass(cls);
38610         }
38611     }
38612
38613     
38614     
38615 });//<Script type="text/javascript">
38616
38617 /*
38618  * Based on:
38619  * Ext JS Library 1.1.1
38620  * Copyright(c) 2006-2007, Ext JS, LLC.
38621  *
38622  * Originally Released Under LGPL - original licence link has changed is not relivant.
38623  *
38624  * Fork - LGPL
38625  * <script type="text/javascript">
38626  */
38627  
38628
38629 /**
38630  * @class Roo.tree.ColumnTree
38631  * @extends Roo.tree.TreePanel
38632  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38633  * @cfg {int} borderWidth  compined right/left border allowance
38634  * @constructor
38635  * @param {String/HTMLElement/Element} el The container element
38636  * @param {Object} config
38637  */
38638 Roo.tree.ColumnTree =  function(el, config)
38639 {
38640    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38641    this.addEvents({
38642         /**
38643         * @event resize
38644         * Fire this event on a container when it resizes
38645         * @param {int} w Width
38646         * @param {int} h Height
38647         */
38648        "resize" : true
38649     });
38650     this.on('resize', this.onResize, this);
38651 };
38652
38653 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38654     //lines:false,
38655     
38656     
38657     borderWidth: Roo.isBorderBox ? 0 : 2, 
38658     headEls : false,
38659     
38660     render : function(){
38661         // add the header.....
38662        
38663         Roo.tree.ColumnTree.superclass.render.apply(this);
38664         
38665         this.el.addClass('x-column-tree');
38666         
38667         this.headers = this.el.createChild(
38668             {cls:'x-tree-headers'},this.innerCt.dom);
38669    
38670         var cols = this.columns, c;
38671         var totalWidth = 0;
38672         this.headEls = [];
38673         var  len = cols.length;
38674         for(var i = 0; i < len; i++){
38675              c = cols[i];
38676              totalWidth += c.width;
38677             this.headEls.push(this.headers.createChild({
38678                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38679                  cn: {
38680                      cls:'x-tree-hd-text',
38681                      html: c.header
38682                  },
38683                  style:'width:'+(c.width-this.borderWidth)+'px;'
38684              }));
38685         }
38686         this.headers.createChild({cls:'x-clear'});
38687         // prevent floats from wrapping when clipped
38688         this.headers.setWidth(totalWidth);
38689         //this.innerCt.setWidth(totalWidth);
38690         this.innerCt.setStyle({ overflow: 'auto' });
38691         this.onResize(this.width, this.height);
38692              
38693         
38694     },
38695     onResize : function(w,h)
38696     {
38697         this.height = h;
38698         this.width = w;
38699         // resize cols..
38700         this.innerCt.setWidth(this.width);
38701         this.innerCt.setHeight(this.height-20);
38702         
38703         // headers...
38704         var cols = this.columns, c;
38705         var totalWidth = 0;
38706         var expEl = false;
38707         var len = cols.length;
38708         for(var i = 0; i < len; i++){
38709             c = cols[i];
38710             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38711                 // it's the expander..
38712                 expEl  = this.headEls[i];
38713                 continue;
38714             }
38715             totalWidth += c.width;
38716             
38717         }
38718         if (expEl) {
38719             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38720         }
38721         this.headers.setWidth(w-20);
38722
38723         
38724         
38725         
38726     }
38727 });
38728 /*
38729  * Based on:
38730  * Ext JS Library 1.1.1
38731  * Copyright(c) 2006-2007, Ext JS, LLC.
38732  *
38733  * Originally Released Under LGPL - original licence link has changed is not relivant.
38734  *
38735  * Fork - LGPL
38736  * <script type="text/javascript">
38737  */
38738  
38739 /**
38740  * @class Roo.menu.Menu
38741  * @extends Roo.util.Observable
38742  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38743  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38744  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38745  * @constructor
38746  * Creates a new Menu
38747  * @param {Object} config Configuration options
38748  */
38749 Roo.menu.Menu = function(config){
38750     
38751     Roo.menu.Menu.superclass.constructor.call(this, config);
38752     
38753     this.id = this.id || Roo.id();
38754     this.addEvents({
38755         /**
38756          * @event beforeshow
38757          * Fires before this menu is displayed
38758          * @param {Roo.menu.Menu} this
38759          */
38760         beforeshow : true,
38761         /**
38762          * @event beforehide
38763          * Fires before this menu is hidden
38764          * @param {Roo.menu.Menu} this
38765          */
38766         beforehide : true,
38767         /**
38768          * @event show
38769          * Fires after this menu is displayed
38770          * @param {Roo.menu.Menu} this
38771          */
38772         show : true,
38773         /**
38774          * @event hide
38775          * Fires after this menu is hidden
38776          * @param {Roo.menu.Menu} this
38777          */
38778         hide : true,
38779         /**
38780          * @event click
38781          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38782          * @param {Roo.menu.Menu} this
38783          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38784          * @param {Roo.EventObject} e
38785          */
38786         click : true,
38787         /**
38788          * @event mouseover
38789          * Fires when the mouse is hovering over this menu
38790          * @param {Roo.menu.Menu} this
38791          * @param {Roo.EventObject} e
38792          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38793          */
38794         mouseover : true,
38795         /**
38796          * @event mouseout
38797          * Fires when the mouse exits this menu
38798          * @param {Roo.menu.Menu} this
38799          * @param {Roo.EventObject} e
38800          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38801          */
38802         mouseout : true,
38803         /**
38804          * @event itemclick
38805          * Fires when a menu item contained in this menu is clicked
38806          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38807          * @param {Roo.EventObject} e
38808          */
38809         itemclick: true
38810     });
38811     if (this.registerMenu) {
38812         Roo.menu.MenuMgr.register(this);
38813     }
38814     
38815     var mis = this.items;
38816     this.items = new Roo.util.MixedCollection();
38817     if(mis){
38818         this.add.apply(this, mis);
38819     }
38820 };
38821
38822 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38823     /**
38824      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38825      */
38826     minWidth : 120,
38827     /**
38828      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38829      * for bottom-right shadow (defaults to "sides")
38830      */
38831     shadow : "sides",
38832     /**
38833      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38834      * this menu (defaults to "tl-tr?")
38835      */
38836     subMenuAlign : "tl-tr?",
38837     /**
38838      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38839      * relative to its element of origin (defaults to "tl-bl?")
38840      */
38841     defaultAlign : "tl-bl?",
38842     /**
38843      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38844      */
38845     allowOtherMenus : false,
38846     /**
38847      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38848      */
38849     registerMenu : true,
38850
38851     hidden:true,
38852
38853     // private
38854     render : function(){
38855         if(this.el){
38856             return;
38857         }
38858         var el = this.el = new Roo.Layer({
38859             cls: "x-menu",
38860             shadow:this.shadow,
38861             constrain: false,
38862             parentEl: this.parentEl || document.body,
38863             zindex:15000
38864         });
38865
38866         this.keyNav = new Roo.menu.MenuNav(this);
38867
38868         if(this.plain){
38869             el.addClass("x-menu-plain");
38870         }
38871         if(this.cls){
38872             el.addClass(this.cls);
38873         }
38874         // generic focus element
38875         this.focusEl = el.createChild({
38876             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38877         });
38878         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38879         //disabling touch- as it's causing issues ..
38880         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38881         ul.on('click'   , this.onClick, this);
38882         
38883         
38884         ul.on("mouseover", this.onMouseOver, this);
38885         ul.on("mouseout", this.onMouseOut, this);
38886         this.items.each(function(item){
38887             if (item.hidden) {
38888                 return;
38889             }
38890             
38891             var li = document.createElement("li");
38892             li.className = "x-menu-list-item";
38893             ul.dom.appendChild(li);
38894             item.render(li, this);
38895         }, this);
38896         this.ul = ul;
38897         this.autoWidth();
38898     },
38899
38900     // private
38901     autoWidth : function(){
38902         var el = this.el, ul = this.ul;
38903         if(!el){
38904             return;
38905         }
38906         var w = this.width;
38907         if(w){
38908             el.setWidth(w);
38909         }else if(Roo.isIE){
38910             el.setWidth(this.minWidth);
38911             var t = el.dom.offsetWidth; // force recalc
38912             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38913         }
38914     },
38915
38916     // private
38917     delayAutoWidth : function(){
38918         if(this.rendered){
38919             if(!this.awTask){
38920                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38921             }
38922             this.awTask.delay(20);
38923         }
38924     },
38925
38926     // private
38927     findTargetItem : function(e){
38928         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38929         if(t && t.menuItemId){
38930             return this.items.get(t.menuItemId);
38931         }
38932     },
38933
38934     // private
38935     onClick : function(e){
38936         Roo.log("menu.onClick");
38937         var t = this.findTargetItem(e);
38938         if(!t){
38939             return;
38940         }
38941         Roo.log(e);
38942         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38943             if(t == this.activeItem && t.shouldDeactivate(e)){
38944                 this.activeItem.deactivate();
38945                 delete this.activeItem;
38946                 return;
38947             }
38948             if(t.canActivate){
38949                 this.setActiveItem(t, true);
38950             }
38951             return;
38952             
38953             
38954         }
38955         
38956         t.onClick(e);
38957         this.fireEvent("click", this, t, e);
38958     },
38959
38960     // private
38961     setActiveItem : function(item, autoExpand){
38962         if(item != this.activeItem){
38963             if(this.activeItem){
38964                 this.activeItem.deactivate();
38965             }
38966             this.activeItem = item;
38967             item.activate(autoExpand);
38968         }else if(autoExpand){
38969             item.expandMenu();
38970         }
38971     },
38972
38973     // private
38974     tryActivate : function(start, step){
38975         var items = this.items;
38976         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38977             var item = items.get(i);
38978             if(!item.disabled && item.canActivate){
38979                 this.setActiveItem(item, false);
38980                 return item;
38981             }
38982         }
38983         return false;
38984     },
38985
38986     // private
38987     onMouseOver : function(e){
38988         var t;
38989         if(t = this.findTargetItem(e)){
38990             if(t.canActivate && !t.disabled){
38991                 this.setActiveItem(t, true);
38992             }
38993         }
38994         this.fireEvent("mouseover", this, e, t);
38995     },
38996
38997     // private
38998     onMouseOut : function(e){
38999         var t;
39000         if(t = this.findTargetItem(e)){
39001             if(t == this.activeItem && t.shouldDeactivate(e)){
39002                 this.activeItem.deactivate();
39003                 delete this.activeItem;
39004             }
39005         }
39006         this.fireEvent("mouseout", this, e, t);
39007     },
39008
39009     /**
39010      * Read-only.  Returns true if the menu is currently displayed, else false.
39011      * @type Boolean
39012      */
39013     isVisible : function(){
39014         return this.el && !this.hidden;
39015     },
39016
39017     /**
39018      * Displays this menu relative to another element
39019      * @param {String/HTMLElement/Roo.Element} element The element to align to
39020      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
39021      * the element (defaults to this.defaultAlign)
39022      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39023      */
39024     show : function(el, pos, parentMenu){
39025         this.parentMenu = parentMenu;
39026         if(!this.el){
39027             this.render();
39028         }
39029         this.fireEvent("beforeshow", this);
39030         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
39031     },
39032
39033     /**
39034      * Displays this menu at a specific xy position
39035      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
39036      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39037      */
39038     showAt : function(xy, parentMenu, /* private: */_e){
39039         this.parentMenu = parentMenu;
39040         if(!this.el){
39041             this.render();
39042         }
39043         if(_e !== false){
39044             this.fireEvent("beforeshow", this);
39045             xy = this.el.adjustForConstraints(xy);
39046         }
39047         this.el.setXY(xy);
39048         this.el.show();
39049         this.hidden = false;
39050         this.focus();
39051         this.fireEvent("show", this);
39052     },
39053
39054     focus : function(){
39055         if(!this.hidden){
39056             this.doFocus.defer(50, this);
39057         }
39058     },
39059
39060     doFocus : function(){
39061         if(!this.hidden){
39062             this.focusEl.focus();
39063         }
39064     },
39065
39066     /**
39067      * Hides this menu and optionally all parent menus
39068      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
39069      */
39070     hide : function(deep){
39071         if(this.el && this.isVisible()){
39072             this.fireEvent("beforehide", this);
39073             if(this.activeItem){
39074                 this.activeItem.deactivate();
39075                 this.activeItem = null;
39076             }
39077             this.el.hide();
39078             this.hidden = true;
39079             this.fireEvent("hide", this);
39080         }
39081         if(deep === true && this.parentMenu){
39082             this.parentMenu.hide(true);
39083         }
39084     },
39085
39086     /**
39087      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
39088      * Any of the following are valid:
39089      * <ul>
39090      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
39091      * <li>An HTMLElement object which will be converted to a menu item</li>
39092      * <li>A menu item config object that will be created as a new menu item</li>
39093      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
39094      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
39095      * </ul>
39096      * Usage:
39097      * <pre><code>
39098 // Create the menu
39099 var menu = new Roo.menu.Menu();
39100
39101 // Create a menu item to add by reference
39102 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
39103
39104 // Add a bunch of items at once using different methods.
39105 // Only the last item added will be returned.
39106 var item = menu.add(
39107     menuItem,                // add existing item by ref
39108     'Dynamic Item',          // new TextItem
39109     '-',                     // new separator
39110     { text: 'Config Item' }  // new item by config
39111 );
39112 </code></pre>
39113      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
39114      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
39115      */
39116     add : function(){
39117         var a = arguments, l = a.length, item;
39118         for(var i = 0; i < l; i++){
39119             var el = a[i];
39120             if ((typeof(el) == "object") && el.xtype && el.xns) {
39121                 el = Roo.factory(el, Roo.menu);
39122             }
39123             
39124             if(el.render){ // some kind of Item
39125                 item = this.addItem(el);
39126             }else if(typeof el == "string"){ // string
39127                 if(el == "separator" || el == "-"){
39128                     item = this.addSeparator();
39129                 }else{
39130                     item = this.addText(el);
39131                 }
39132             }else if(el.tagName || el.el){ // element
39133                 item = this.addElement(el);
39134             }else if(typeof el == "object"){ // must be menu item config?
39135                 item = this.addMenuItem(el);
39136             }
39137         }
39138         return item;
39139     },
39140
39141     /**
39142      * Returns this menu's underlying {@link Roo.Element} object
39143      * @return {Roo.Element} The element
39144      */
39145     getEl : function(){
39146         if(!this.el){
39147             this.render();
39148         }
39149         return this.el;
39150     },
39151
39152     /**
39153      * Adds a separator bar to the menu
39154      * @return {Roo.menu.Item} The menu item that was added
39155      */
39156     addSeparator : function(){
39157         return this.addItem(new Roo.menu.Separator());
39158     },
39159
39160     /**
39161      * Adds an {@link Roo.Element} object to the menu
39162      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
39163      * @return {Roo.menu.Item} The menu item that was added
39164      */
39165     addElement : function(el){
39166         return this.addItem(new Roo.menu.BaseItem(el));
39167     },
39168
39169     /**
39170      * Adds an existing object based on {@link Roo.menu.Item} to the menu
39171      * @param {Roo.menu.Item} item The menu item to add
39172      * @return {Roo.menu.Item} The menu item that was added
39173      */
39174     addItem : function(item){
39175         this.items.add(item);
39176         if(this.ul){
39177             var li = document.createElement("li");
39178             li.className = "x-menu-list-item";
39179             this.ul.dom.appendChild(li);
39180             item.render(li, this);
39181             this.delayAutoWidth();
39182         }
39183         return item;
39184     },
39185
39186     /**
39187      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
39188      * @param {Object} config A MenuItem config object
39189      * @return {Roo.menu.Item} The menu item that was added
39190      */
39191     addMenuItem : function(config){
39192         if(!(config instanceof Roo.menu.Item)){
39193             if(typeof config.checked == "boolean"){ // must be check menu item config?
39194                 config = new Roo.menu.CheckItem(config);
39195             }else{
39196                 config = new Roo.menu.Item(config);
39197             }
39198         }
39199         return this.addItem(config);
39200     },
39201
39202     /**
39203      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
39204      * @param {String} text The text to display in the menu item
39205      * @return {Roo.menu.Item} The menu item that was added
39206      */
39207     addText : function(text){
39208         return this.addItem(new Roo.menu.TextItem({ text : text }));
39209     },
39210
39211     /**
39212      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
39213      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
39214      * @param {Roo.menu.Item} item The menu item to add
39215      * @return {Roo.menu.Item} The menu item that was added
39216      */
39217     insert : function(index, item){
39218         this.items.insert(index, item);
39219         if(this.ul){
39220             var li = document.createElement("li");
39221             li.className = "x-menu-list-item";
39222             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
39223             item.render(li, this);
39224             this.delayAutoWidth();
39225         }
39226         return item;
39227     },
39228
39229     /**
39230      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
39231      * @param {Roo.menu.Item} item The menu item to remove
39232      */
39233     remove : function(item){
39234         this.items.removeKey(item.id);
39235         item.destroy();
39236     },
39237
39238     /**
39239      * Removes and destroys all items in the menu
39240      */
39241     removeAll : function(){
39242         var f;
39243         while(f = this.items.first()){
39244             this.remove(f);
39245         }
39246     }
39247 });
39248
39249 // MenuNav is a private utility class used internally by the Menu
39250 Roo.menu.MenuNav = function(menu){
39251     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
39252     this.scope = this.menu = menu;
39253 };
39254
39255 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
39256     doRelay : function(e, h){
39257         var k = e.getKey();
39258         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
39259             this.menu.tryActivate(0, 1);
39260             return false;
39261         }
39262         return h.call(this.scope || this, e, this.menu);
39263     },
39264
39265     up : function(e, m){
39266         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
39267             m.tryActivate(m.items.length-1, -1);
39268         }
39269     },
39270
39271     down : function(e, m){
39272         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
39273             m.tryActivate(0, 1);
39274         }
39275     },
39276
39277     right : function(e, m){
39278         if(m.activeItem){
39279             m.activeItem.expandMenu(true);
39280         }
39281     },
39282
39283     left : function(e, m){
39284         m.hide();
39285         if(m.parentMenu && m.parentMenu.activeItem){
39286             m.parentMenu.activeItem.activate();
39287         }
39288     },
39289
39290     enter : function(e, m){
39291         if(m.activeItem){
39292             e.stopPropagation();
39293             m.activeItem.onClick(e);
39294             m.fireEvent("click", this, m.activeItem);
39295             return true;
39296         }
39297     }
39298 });/*
39299  * Based on:
39300  * Ext JS Library 1.1.1
39301  * Copyright(c) 2006-2007, Ext JS, LLC.
39302  *
39303  * Originally Released Under LGPL - original licence link has changed is not relivant.
39304  *
39305  * Fork - LGPL
39306  * <script type="text/javascript">
39307  */
39308  
39309 /**
39310  * @class Roo.menu.MenuMgr
39311  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
39312  * @static
39313  */
39314 Roo.menu.MenuMgr = function(){
39315    var menus, active, groups = {}, attached = false, lastShow = new Date();
39316
39317    // private - called when first menu is created
39318    function init(){
39319        menus = {};
39320        active = new Roo.util.MixedCollection();
39321        Roo.get(document).addKeyListener(27, function(){
39322            if(active.length > 0){
39323                hideAll();
39324            }
39325        });
39326    }
39327
39328    // private
39329    function hideAll(){
39330        if(active && active.length > 0){
39331            var c = active.clone();
39332            c.each(function(m){
39333                m.hide();
39334            });
39335        }
39336    }
39337
39338    // private
39339    function onHide(m){
39340        active.remove(m);
39341        if(active.length < 1){
39342            Roo.get(document).un("mousedown", onMouseDown);
39343            attached = false;
39344        }
39345    }
39346
39347    // private
39348    function onShow(m){
39349        var last = active.last();
39350        lastShow = new Date();
39351        active.add(m);
39352        if(!attached){
39353            Roo.get(document).on("mousedown", onMouseDown);
39354            attached = true;
39355        }
39356        if(m.parentMenu){
39357           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39358           m.parentMenu.activeChild = m;
39359        }else if(last && last.isVisible()){
39360           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39361        }
39362    }
39363
39364    // private
39365    function onBeforeHide(m){
39366        if(m.activeChild){
39367            m.activeChild.hide();
39368        }
39369        if(m.autoHideTimer){
39370            clearTimeout(m.autoHideTimer);
39371            delete m.autoHideTimer;
39372        }
39373    }
39374
39375    // private
39376    function onBeforeShow(m){
39377        var pm = m.parentMenu;
39378        if(!pm && !m.allowOtherMenus){
39379            hideAll();
39380        }else if(pm && pm.activeChild && active != m){
39381            pm.activeChild.hide();
39382        }
39383    }
39384
39385    // private
39386    function onMouseDown(e){
39387        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39388            hideAll();
39389        }
39390    }
39391
39392    // private
39393    function onBeforeCheck(mi, state){
39394        if(state){
39395            var g = groups[mi.group];
39396            for(var i = 0, l = g.length; i < l; i++){
39397                if(g[i] != mi){
39398                    g[i].setChecked(false);
39399                }
39400            }
39401        }
39402    }
39403
39404    return {
39405
39406        /**
39407         * Hides all menus that are currently visible
39408         */
39409        hideAll : function(){
39410             hideAll();  
39411        },
39412
39413        // private
39414        register : function(menu){
39415            if(!menus){
39416                init();
39417            }
39418            menus[menu.id] = menu;
39419            menu.on("beforehide", onBeforeHide);
39420            menu.on("hide", onHide);
39421            menu.on("beforeshow", onBeforeShow);
39422            menu.on("show", onShow);
39423            var g = menu.group;
39424            if(g && menu.events["checkchange"]){
39425                if(!groups[g]){
39426                    groups[g] = [];
39427                }
39428                groups[g].push(menu);
39429                menu.on("checkchange", onCheck);
39430            }
39431        },
39432
39433         /**
39434          * Returns a {@link Roo.menu.Menu} object
39435          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39436          * be used to generate and return a new Menu instance.
39437          */
39438        get : function(menu){
39439            if(typeof menu == "string"){ // menu id
39440                return menus[menu];
39441            }else if(menu.events){  // menu instance
39442                return menu;
39443            }else if(typeof menu.length == 'number'){ // array of menu items?
39444                return new Roo.menu.Menu({items:menu});
39445            }else{ // otherwise, must be a config
39446                return new Roo.menu.Menu(menu);
39447            }
39448        },
39449
39450        // private
39451        unregister : function(menu){
39452            delete menus[menu.id];
39453            menu.un("beforehide", onBeforeHide);
39454            menu.un("hide", onHide);
39455            menu.un("beforeshow", onBeforeShow);
39456            menu.un("show", onShow);
39457            var g = menu.group;
39458            if(g && menu.events["checkchange"]){
39459                groups[g].remove(menu);
39460                menu.un("checkchange", onCheck);
39461            }
39462        },
39463
39464        // private
39465        registerCheckable : function(menuItem){
39466            var g = menuItem.group;
39467            if(g){
39468                if(!groups[g]){
39469                    groups[g] = [];
39470                }
39471                groups[g].push(menuItem);
39472                menuItem.on("beforecheckchange", onBeforeCheck);
39473            }
39474        },
39475
39476        // private
39477        unregisterCheckable : function(menuItem){
39478            var g = menuItem.group;
39479            if(g){
39480                groups[g].remove(menuItem);
39481                menuItem.un("beforecheckchange", onBeforeCheck);
39482            }
39483        }
39484    };
39485 }();/*
39486  * Based on:
39487  * Ext JS Library 1.1.1
39488  * Copyright(c) 2006-2007, Ext JS, LLC.
39489  *
39490  * Originally Released Under LGPL - original licence link has changed is not relivant.
39491  *
39492  * Fork - LGPL
39493  * <script type="text/javascript">
39494  */
39495  
39496
39497 /**
39498  * @class Roo.menu.BaseItem
39499  * @extends Roo.Component
39500  * @abstract
39501  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39502  * management and base configuration options shared by all menu components.
39503  * @constructor
39504  * Creates a new BaseItem
39505  * @param {Object} config Configuration options
39506  */
39507 Roo.menu.BaseItem = function(config){
39508     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39509
39510     this.addEvents({
39511         /**
39512          * @event click
39513          * Fires when this item is clicked
39514          * @param {Roo.menu.BaseItem} this
39515          * @param {Roo.EventObject} e
39516          */
39517         click: true,
39518         /**
39519          * @event activate
39520          * Fires when this item is activated
39521          * @param {Roo.menu.BaseItem} this
39522          */
39523         activate : true,
39524         /**
39525          * @event deactivate
39526          * Fires when this item is deactivated
39527          * @param {Roo.menu.BaseItem} this
39528          */
39529         deactivate : true
39530     });
39531
39532     if(this.handler){
39533         this.on("click", this.handler, this.scope, true);
39534     }
39535 };
39536
39537 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39538     /**
39539      * @cfg {Function} handler
39540      * A function that will handle the click event of this menu item (defaults to undefined)
39541      */
39542     /**
39543      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39544      */
39545     canActivate : false,
39546     
39547      /**
39548      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39549      */
39550     hidden: false,
39551     
39552     /**
39553      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39554      */
39555     activeClass : "x-menu-item-active",
39556     /**
39557      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39558      */
39559     hideOnClick : true,
39560     /**
39561      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39562      */
39563     hideDelay : 100,
39564
39565     // private
39566     ctype: "Roo.menu.BaseItem",
39567
39568     // private
39569     actionMode : "container",
39570
39571     // private
39572     render : function(container, parentMenu){
39573         this.parentMenu = parentMenu;
39574         Roo.menu.BaseItem.superclass.render.call(this, container);
39575         this.container.menuItemId = this.id;
39576     },
39577
39578     // private
39579     onRender : function(container, position){
39580         this.el = Roo.get(this.el);
39581         container.dom.appendChild(this.el.dom);
39582     },
39583
39584     // private
39585     onClick : function(e){
39586         if(!this.disabled && this.fireEvent("click", this, e) !== false
39587                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39588             this.handleClick(e);
39589         }else{
39590             e.stopEvent();
39591         }
39592     },
39593
39594     // private
39595     activate : function(){
39596         if(this.disabled){
39597             return false;
39598         }
39599         var li = this.container;
39600         li.addClass(this.activeClass);
39601         this.region = li.getRegion().adjust(2, 2, -2, -2);
39602         this.fireEvent("activate", this);
39603         return true;
39604     },
39605
39606     // private
39607     deactivate : function(){
39608         this.container.removeClass(this.activeClass);
39609         this.fireEvent("deactivate", this);
39610     },
39611
39612     // private
39613     shouldDeactivate : function(e){
39614         return !this.region || !this.region.contains(e.getPoint());
39615     },
39616
39617     // private
39618     handleClick : function(e){
39619         if(this.hideOnClick){
39620             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39621         }
39622     },
39623
39624     // private
39625     expandMenu : function(autoActivate){
39626         // do nothing
39627     },
39628
39629     // private
39630     hideMenu : function(){
39631         // do nothing
39632     }
39633 });/*
39634  * Based on:
39635  * Ext JS Library 1.1.1
39636  * Copyright(c) 2006-2007, Ext JS, LLC.
39637  *
39638  * Originally Released Under LGPL - original licence link has changed is not relivant.
39639  *
39640  * Fork - LGPL
39641  * <script type="text/javascript">
39642  */
39643  
39644 /**
39645  * @class Roo.menu.Adapter
39646  * @extends Roo.menu.BaseItem
39647  * @abstract
39648  * 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.
39649  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39650  * @constructor
39651  * Creates a new Adapter
39652  * @param {Object} config Configuration options
39653  */
39654 Roo.menu.Adapter = function(component, config){
39655     Roo.menu.Adapter.superclass.constructor.call(this, config);
39656     this.component = component;
39657 };
39658 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39659     // private
39660     canActivate : true,
39661
39662     // private
39663     onRender : function(container, position){
39664         this.component.render(container);
39665         this.el = this.component.getEl();
39666     },
39667
39668     // private
39669     activate : function(){
39670         if(this.disabled){
39671             return false;
39672         }
39673         this.component.focus();
39674         this.fireEvent("activate", this);
39675         return true;
39676     },
39677
39678     // private
39679     deactivate : function(){
39680         this.fireEvent("deactivate", this);
39681     },
39682
39683     // private
39684     disable : function(){
39685         this.component.disable();
39686         Roo.menu.Adapter.superclass.disable.call(this);
39687     },
39688
39689     // private
39690     enable : function(){
39691         this.component.enable();
39692         Roo.menu.Adapter.superclass.enable.call(this);
39693     }
39694 });/*
39695  * Based on:
39696  * Ext JS Library 1.1.1
39697  * Copyright(c) 2006-2007, Ext JS, LLC.
39698  *
39699  * Originally Released Under LGPL - original licence link has changed is not relivant.
39700  *
39701  * Fork - LGPL
39702  * <script type="text/javascript">
39703  */
39704
39705 /**
39706  * @class Roo.menu.TextItem
39707  * @extends Roo.menu.BaseItem
39708  * Adds a static text string to a menu, usually used as either a heading or group separator.
39709  * Note: old style constructor with text is still supported.
39710  * 
39711  * @constructor
39712  * Creates a new TextItem
39713  * @param {Object} cfg Configuration
39714  */
39715 Roo.menu.TextItem = function(cfg){
39716     if (typeof(cfg) == 'string') {
39717         this.text = cfg;
39718     } else {
39719         Roo.apply(this,cfg);
39720     }
39721     
39722     Roo.menu.TextItem.superclass.constructor.call(this);
39723 };
39724
39725 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39726     /**
39727      * @cfg {String} text Text to show on item.
39728      */
39729     text : '',
39730     
39731     /**
39732      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39733      */
39734     hideOnClick : false,
39735     /**
39736      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39737      */
39738     itemCls : "x-menu-text",
39739
39740     // private
39741     onRender : function(){
39742         var s = document.createElement("span");
39743         s.className = this.itemCls;
39744         s.innerHTML = this.text;
39745         this.el = s;
39746         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39747     }
39748 });/*
39749  * Based on:
39750  * Ext JS Library 1.1.1
39751  * Copyright(c) 2006-2007, Ext JS, LLC.
39752  *
39753  * Originally Released Under LGPL - original licence link has changed is not relivant.
39754  *
39755  * Fork - LGPL
39756  * <script type="text/javascript">
39757  */
39758
39759 /**
39760  * @class Roo.menu.Separator
39761  * @extends Roo.menu.BaseItem
39762  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39763  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39764  * @constructor
39765  * @param {Object} config Configuration options
39766  */
39767 Roo.menu.Separator = function(config){
39768     Roo.menu.Separator.superclass.constructor.call(this, config);
39769 };
39770
39771 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39772     /**
39773      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39774      */
39775     itemCls : "x-menu-sep",
39776     /**
39777      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39778      */
39779     hideOnClick : false,
39780
39781     // private
39782     onRender : function(li){
39783         var s = document.createElement("span");
39784         s.className = this.itemCls;
39785         s.innerHTML = "&#160;";
39786         this.el = s;
39787         li.addClass("x-menu-sep-li");
39788         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39789     }
39790 });/*
39791  * Based on:
39792  * Ext JS Library 1.1.1
39793  * Copyright(c) 2006-2007, Ext JS, LLC.
39794  *
39795  * Originally Released Under LGPL - original licence link has changed is not relivant.
39796  *
39797  * Fork - LGPL
39798  * <script type="text/javascript">
39799  */
39800 /**
39801  * @class Roo.menu.Item
39802  * @extends Roo.menu.BaseItem
39803  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39804  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39805  * activation and click handling.
39806  * @constructor
39807  * Creates a new Item
39808  * @param {Object} config Configuration options
39809  */
39810 Roo.menu.Item = function(config){
39811     Roo.menu.Item.superclass.constructor.call(this, config);
39812     if(this.menu){
39813         this.menu = Roo.menu.MenuMgr.get(this.menu);
39814     }
39815 };
39816 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39817     /**
39818      * @cfg {Roo.menu.Menu} menu
39819      * A Sub menu
39820      */
39821     /**
39822      * @cfg {String} text
39823      * The text to show on the menu item.
39824      */
39825     text: '',
39826      /**
39827      * @cfg {String} HTML to render in menu
39828      * The text to show on the menu item (HTML version).
39829      */
39830     html: '',
39831     /**
39832      * @cfg {String} icon
39833      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39834      */
39835     icon: undefined,
39836     /**
39837      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39838      */
39839     itemCls : "x-menu-item",
39840     /**
39841      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39842      */
39843     canActivate : true,
39844     /**
39845      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39846      */
39847     showDelay: 200,
39848     // doc'd in BaseItem
39849     hideDelay: 200,
39850
39851     // private
39852     ctype: "Roo.menu.Item",
39853     
39854     // private
39855     onRender : function(container, position){
39856         var el = document.createElement("a");
39857         el.hideFocus = true;
39858         el.unselectable = "on";
39859         el.href = this.href || "#";
39860         if(this.hrefTarget){
39861             el.target = this.hrefTarget;
39862         }
39863         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39864         
39865         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39866         
39867         el.innerHTML = String.format(
39868                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39869                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39870         this.el = el;
39871         Roo.menu.Item.superclass.onRender.call(this, container, position);
39872     },
39873
39874     /**
39875      * Sets the text to display in this menu item
39876      * @param {String} text The text to display
39877      * @param {Boolean} isHTML true to indicate text is pure html.
39878      */
39879     setText : function(text, isHTML){
39880         if (isHTML) {
39881             this.html = text;
39882         } else {
39883             this.text = text;
39884             this.html = '';
39885         }
39886         if(this.rendered){
39887             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39888      
39889             this.el.update(String.format(
39890                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39891                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39892             this.parentMenu.autoWidth();
39893         }
39894     },
39895
39896     // private
39897     handleClick : function(e){
39898         if(!this.href){ // if no link defined, stop the event automatically
39899             e.stopEvent();
39900         }
39901         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39902     },
39903
39904     // private
39905     activate : function(autoExpand){
39906         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39907             this.focus();
39908             if(autoExpand){
39909                 this.expandMenu();
39910             }
39911         }
39912         return true;
39913     },
39914
39915     // private
39916     shouldDeactivate : function(e){
39917         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39918             if(this.menu && this.menu.isVisible()){
39919                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39920             }
39921             return true;
39922         }
39923         return false;
39924     },
39925
39926     // private
39927     deactivate : function(){
39928         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39929         this.hideMenu();
39930     },
39931
39932     // private
39933     expandMenu : function(autoActivate){
39934         if(!this.disabled && this.menu){
39935             clearTimeout(this.hideTimer);
39936             delete this.hideTimer;
39937             if(!this.menu.isVisible() && !this.showTimer){
39938                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39939             }else if (this.menu.isVisible() && autoActivate){
39940                 this.menu.tryActivate(0, 1);
39941             }
39942         }
39943     },
39944
39945     // private
39946     deferExpand : function(autoActivate){
39947         delete this.showTimer;
39948         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39949         if(autoActivate){
39950             this.menu.tryActivate(0, 1);
39951         }
39952     },
39953
39954     // private
39955     hideMenu : function(){
39956         clearTimeout(this.showTimer);
39957         delete this.showTimer;
39958         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39959             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39960         }
39961     },
39962
39963     // private
39964     deferHide : function(){
39965         delete this.hideTimer;
39966         this.menu.hide();
39967     }
39968 });/*
39969  * Based on:
39970  * Ext JS Library 1.1.1
39971  * Copyright(c) 2006-2007, Ext JS, LLC.
39972  *
39973  * Originally Released Under LGPL - original licence link has changed is not relivant.
39974  *
39975  * Fork - LGPL
39976  * <script type="text/javascript">
39977  */
39978  
39979 /**
39980  * @class Roo.menu.CheckItem
39981  * @extends Roo.menu.Item
39982  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39983  * @constructor
39984  * Creates a new CheckItem
39985  * @param {Object} config Configuration options
39986  */
39987 Roo.menu.CheckItem = function(config){
39988     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39989     this.addEvents({
39990         /**
39991          * @event beforecheckchange
39992          * Fires before the checked value is set, providing an opportunity to cancel if needed
39993          * @param {Roo.menu.CheckItem} this
39994          * @param {Boolean} checked The new checked value that will be set
39995          */
39996         "beforecheckchange" : true,
39997         /**
39998          * @event checkchange
39999          * Fires after the checked value has been set
40000          * @param {Roo.menu.CheckItem} this
40001          * @param {Boolean} checked The checked value that was set
40002          */
40003         "checkchange" : true
40004     });
40005     if(this.checkHandler){
40006         this.on('checkchange', this.checkHandler, this.scope);
40007     }
40008 };
40009 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
40010     /**
40011      * @cfg {String} group
40012      * All check items with the same group name will automatically be grouped into a single-select
40013      * radio button group (defaults to '')
40014      */
40015     /**
40016      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
40017      */
40018     itemCls : "x-menu-item x-menu-check-item",
40019     /**
40020      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
40021      */
40022     groupClass : "x-menu-group-item",
40023
40024     /**
40025      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
40026      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
40027      * initialized with checked = true will be rendered as checked.
40028      */
40029     checked: false,
40030
40031     // private
40032     ctype: "Roo.menu.CheckItem",
40033
40034     // private
40035     onRender : function(c){
40036         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
40037         if(this.group){
40038             this.el.addClass(this.groupClass);
40039         }
40040         Roo.menu.MenuMgr.registerCheckable(this);
40041         if(this.checked){
40042             this.checked = false;
40043             this.setChecked(true, true);
40044         }
40045     },
40046
40047     // private
40048     destroy : function(){
40049         if(this.rendered){
40050             Roo.menu.MenuMgr.unregisterCheckable(this);
40051         }
40052         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
40053     },
40054
40055     /**
40056      * Set the checked state of this item
40057      * @param {Boolean} checked The new checked value
40058      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
40059      */
40060     setChecked : function(state, suppressEvent){
40061         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
40062             if(this.container){
40063                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
40064             }
40065             this.checked = state;
40066             if(suppressEvent !== true){
40067                 this.fireEvent("checkchange", this, state);
40068             }
40069         }
40070     },
40071
40072     // private
40073     handleClick : function(e){
40074        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
40075            this.setChecked(!this.checked);
40076        }
40077        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
40078     }
40079 });/*
40080  * Based on:
40081  * Ext JS Library 1.1.1
40082  * Copyright(c) 2006-2007, Ext JS, LLC.
40083  *
40084  * Originally Released Under LGPL - original licence link has changed is not relivant.
40085  *
40086  * Fork - LGPL
40087  * <script type="text/javascript">
40088  */
40089  
40090 /**
40091  * @class Roo.menu.DateItem
40092  * @extends Roo.menu.Adapter
40093  * A menu item that wraps the {@link Roo.DatPicker} component.
40094  * @constructor
40095  * Creates a new DateItem
40096  * @param {Object} config Configuration options
40097  */
40098 Roo.menu.DateItem = function(config){
40099     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
40100     /** The Roo.DatePicker object @type Roo.DatePicker */
40101     this.picker = this.component;
40102     this.addEvents({select: true});
40103     
40104     this.picker.on("render", function(picker){
40105         picker.getEl().swallowEvent("click");
40106         picker.container.addClass("x-menu-date-item");
40107     });
40108
40109     this.picker.on("select", this.onSelect, this);
40110 };
40111
40112 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
40113     // private
40114     onSelect : function(picker, date){
40115         this.fireEvent("select", this, date, picker);
40116         Roo.menu.DateItem.superclass.handleClick.call(this);
40117     }
40118 });/*
40119  * Based on:
40120  * Ext JS Library 1.1.1
40121  * Copyright(c) 2006-2007, Ext JS, LLC.
40122  *
40123  * Originally Released Under LGPL - original licence link has changed is not relivant.
40124  *
40125  * Fork - LGPL
40126  * <script type="text/javascript">
40127  */
40128  
40129 /**
40130  * @class Roo.menu.ColorItem
40131  * @extends Roo.menu.Adapter
40132  * A menu item that wraps the {@link Roo.ColorPalette} component.
40133  * @constructor
40134  * Creates a new ColorItem
40135  * @param {Object} config Configuration options
40136  */
40137 Roo.menu.ColorItem = function(config){
40138     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
40139     /** The Roo.ColorPalette object @type Roo.ColorPalette */
40140     this.palette = this.component;
40141     this.relayEvents(this.palette, ["select"]);
40142     if(this.selectHandler){
40143         this.on('select', this.selectHandler, this.scope);
40144     }
40145 };
40146 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
40147  * Based on:
40148  * Ext JS Library 1.1.1
40149  * Copyright(c) 2006-2007, Ext JS, LLC.
40150  *
40151  * Originally Released Under LGPL - original licence link has changed is not relivant.
40152  *
40153  * Fork - LGPL
40154  * <script type="text/javascript">
40155  */
40156  
40157
40158 /**
40159  * @class Roo.menu.DateMenu
40160  * @extends Roo.menu.Menu
40161  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
40162  * @constructor
40163  * Creates a new DateMenu
40164  * @param {Object} config Configuration options
40165  */
40166 Roo.menu.DateMenu = function(config){
40167     Roo.menu.DateMenu.superclass.constructor.call(this, config);
40168     this.plain = true;
40169     var di = new Roo.menu.DateItem(config);
40170     this.add(di);
40171     /**
40172      * The {@link Roo.DatePicker} instance for this DateMenu
40173      * @type DatePicker
40174      */
40175     this.picker = di.picker;
40176     /**
40177      * @event select
40178      * @param {DatePicker} picker
40179      * @param {Date} date
40180      */
40181     this.relayEvents(di, ["select"]);
40182     this.on('beforeshow', function(){
40183         if(this.picker){
40184             this.picker.hideMonthPicker(false);
40185         }
40186     }, this);
40187 };
40188 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
40189     cls:'x-date-menu'
40190 });/*
40191  * Based on:
40192  * Ext JS Library 1.1.1
40193  * Copyright(c) 2006-2007, Ext JS, LLC.
40194  *
40195  * Originally Released Under LGPL - original licence link has changed is not relivant.
40196  *
40197  * Fork - LGPL
40198  * <script type="text/javascript">
40199  */
40200  
40201
40202 /**
40203  * @class Roo.menu.ColorMenu
40204  * @extends Roo.menu.Menu
40205  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
40206  * @constructor
40207  * Creates a new ColorMenu
40208  * @param {Object} config Configuration options
40209  */
40210 Roo.menu.ColorMenu = function(config){
40211     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
40212     this.plain = true;
40213     var ci = new Roo.menu.ColorItem(config);
40214     this.add(ci);
40215     /**
40216      * The {@link Roo.ColorPalette} instance for this ColorMenu
40217      * @type ColorPalette
40218      */
40219     this.palette = ci.palette;
40220     /**
40221      * @event select
40222      * @param {ColorPalette} palette
40223      * @param {String} color
40224      */
40225     this.relayEvents(ci, ["select"]);
40226 };
40227 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
40228  * Based on:
40229  * Ext JS Library 1.1.1
40230  * Copyright(c) 2006-2007, Ext JS, LLC.
40231  *
40232  * Originally Released Under LGPL - original licence link has changed is not relivant.
40233  *
40234  * Fork - LGPL
40235  * <script type="text/javascript">
40236  */
40237  
40238 /**
40239  * @class Roo.form.TextItem
40240  * @extends Roo.BoxComponent
40241  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40242  * @constructor
40243  * Creates a new TextItem
40244  * @param {Object} config Configuration options
40245  */
40246 Roo.form.TextItem = function(config){
40247     Roo.form.TextItem.superclass.constructor.call(this, config);
40248 };
40249
40250 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
40251     
40252     /**
40253      * @cfg {String} tag the tag for this item (default div)
40254      */
40255     tag : 'div',
40256     /**
40257      * @cfg {String} html the content for this item
40258      */
40259     html : '',
40260     
40261     getAutoCreate : function()
40262     {
40263         var cfg = {
40264             id: this.id,
40265             tag: this.tag,
40266             html: this.html,
40267             cls: 'x-form-item'
40268         };
40269         
40270         return cfg;
40271         
40272     },
40273     
40274     onRender : function(ct, position)
40275     {
40276         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
40277         
40278         if(!this.el){
40279             var cfg = this.getAutoCreate();
40280             if(!cfg.name){
40281                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40282             }
40283             if (!cfg.name.length) {
40284                 delete cfg.name;
40285             }
40286             this.el = ct.createChild(cfg, position);
40287         }
40288     },
40289     /*
40290      * setHTML
40291      * @param {String} html update the Contents of the element.
40292      */
40293     setHTML : function(html)
40294     {
40295         this.fieldEl.dom.innerHTML = html;
40296     }
40297     
40298 });/*
40299  * Based on:
40300  * Ext JS Library 1.1.1
40301  * Copyright(c) 2006-2007, Ext JS, LLC.
40302  *
40303  * Originally Released Under LGPL - original licence link has changed is not relivant.
40304  *
40305  * Fork - LGPL
40306  * <script type="text/javascript">
40307  */
40308  
40309 /**
40310  * @class Roo.form.Field
40311  * @extends Roo.BoxComponent
40312  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40313  * @constructor
40314  * Creates a new Field
40315  * @param {Object} config Configuration options
40316  */
40317 Roo.form.Field = function(config){
40318     Roo.form.Field.superclass.constructor.call(this, config);
40319 };
40320
40321 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
40322     /**
40323      * @cfg {String} fieldLabel Label to use when rendering a form.
40324      */
40325        /**
40326      * @cfg {String} qtip Mouse over tip
40327      */
40328      
40329     /**
40330      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40331      */
40332     invalidClass : "x-form-invalid",
40333     /**
40334      * @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")
40335      */
40336     invalidText : "The value in this field is invalid",
40337     /**
40338      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40339      */
40340     focusClass : "x-form-focus",
40341     /**
40342      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40343       automatic validation (defaults to "keyup").
40344      */
40345     validationEvent : "keyup",
40346     /**
40347      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40348      */
40349     validateOnBlur : true,
40350     /**
40351      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40352      */
40353     validationDelay : 250,
40354     /**
40355      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40356      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40357      */
40358     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40359     /**
40360      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40361      */
40362     fieldClass : "x-form-field",
40363     /**
40364      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40365      *<pre>
40366 Value         Description
40367 -----------   ----------------------------------------------------------------------
40368 qtip          Display a quick tip when the user hovers over the field
40369 title         Display a default browser title attribute popup
40370 under         Add a block div beneath the field containing the error text
40371 side          Add an error icon to the right of the field with a popup on hover
40372 [element id]  Add the error text directly to the innerHTML of the specified element
40373 </pre>
40374      */
40375     msgTarget : 'qtip',
40376     /**
40377      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40378      */
40379     msgFx : 'normal',
40380
40381     /**
40382      * @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.
40383      */
40384     readOnly : false,
40385
40386     /**
40387      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40388      */
40389     disabled : false,
40390
40391     /**
40392      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40393      */
40394     inputType : undefined,
40395     
40396     /**
40397      * @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).
40398          */
40399         tabIndex : undefined,
40400         
40401     // private
40402     isFormField : true,
40403
40404     // private
40405     hasFocus : false,
40406     /**
40407      * @property {Roo.Element} fieldEl
40408      * Element Containing the rendered Field (with label etc.)
40409      */
40410     /**
40411      * @cfg {Mixed} value A value to initialize this field with.
40412      */
40413     value : undefined,
40414
40415     /**
40416      * @cfg {String} name The field's HTML name attribute.
40417      */
40418     /**
40419      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40420      */
40421     // private
40422     loadedValue : false,
40423      
40424      
40425         // private ??
40426         initComponent : function(){
40427         Roo.form.Field.superclass.initComponent.call(this);
40428         this.addEvents({
40429             /**
40430              * @event focus
40431              * Fires when this field receives input focus.
40432              * @param {Roo.form.Field} this
40433              */
40434             focus : true,
40435             /**
40436              * @event blur
40437              * Fires when this field loses input focus.
40438              * @param {Roo.form.Field} this
40439              */
40440             blur : true,
40441             /**
40442              * @event specialkey
40443              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40444              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40445              * @param {Roo.form.Field} this
40446              * @param {Roo.EventObject} e The event object
40447              */
40448             specialkey : true,
40449             /**
40450              * @event change
40451              * Fires just before the field blurs if the field value has changed.
40452              * @param {Roo.form.Field} this
40453              * @param {Mixed} newValue The new value
40454              * @param {Mixed} oldValue The original value
40455              */
40456             change : true,
40457             /**
40458              * @event invalid
40459              * Fires after the field has been marked as invalid.
40460              * @param {Roo.form.Field} this
40461              * @param {String} msg The validation message
40462              */
40463             invalid : true,
40464             /**
40465              * @event valid
40466              * Fires after the field has been validated with no errors.
40467              * @param {Roo.form.Field} this
40468              */
40469             valid : true,
40470              /**
40471              * @event keyup
40472              * Fires after the key up
40473              * @param {Roo.form.Field} this
40474              * @param {Roo.EventObject}  e The event Object
40475              */
40476             keyup : true
40477         });
40478     },
40479
40480     /**
40481      * Returns the name attribute of the field if available
40482      * @return {String} name The field name
40483      */
40484     getName: function(){
40485          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40486     },
40487
40488     // private
40489     onRender : function(ct, position){
40490         Roo.form.Field.superclass.onRender.call(this, ct, position);
40491         if(!this.el){
40492             var cfg = this.getAutoCreate();
40493             if(!cfg.name){
40494                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40495             }
40496             if (!cfg.name.length) {
40497                 delete cfg.name;
40498             }
40499             if(this.inputType){
40500                 cfg.type = this.inputType;
40501             }
40502             this.el = ct.createChild(cfg, position);
40503         }
40504         var type = this.el.dom.type;
40505         if(type){
40506             if(type == 'password'){
40507                 type = 'text';
40508             }
40509             this.el.addClass('x-form-'+type);
40510         }
40511         if(this.readOnly){
40512             this.el.dom.readOnly = true;
40513         }
40514         if(this.tabIndex !== undefined){
40515             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40516         }
40517
40518         this.el.addClass([this.fieldClass, this.cls]);
40519         this.initValue();
40520     },
40521
40522     /**
40523      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40524      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40525      * @return {Roo.form.Field} this
40526      */
40527     applyTo : function(target){
40528         this.allowDomMove = false;
40529         this.el = Roo.get(target);
40530         this.render(this.el.dom.parentNode);
40531         return this;
40532     },
40533
40534     // private
40535     initValue : function(){
40536         if(this.value !== undefined){
40537             this.setValue(this.value);
40538         }else if(this.el.dom.value.length > 0){
40539             this.setValue(this.el.dom.value);
40540         }
40541     },
40542
40543     /**
40544      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40545      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40546      */
40547     isDirty : function() {
40548         if(this.disabled) {
40549             return false;
40550         }
40551         return String(this.getValue()) !== String(this.originalValue);
40552     },
40553
40554     /**
40555      * stores the current value in loadedValue
40556      */
40557     resetHasChanged : function()
40558     {
40559         this.loadedValue = String(this.getValue());
40560     },
40561     /**
40562      * checks the current value against the 'loaded' value.
40563      * Note - will return false if 'resetHasChanged' has not been called first.
40564      */
40565     hasChanged : function()
40566     {
40567         if(this.disabled || this.readOnly) {
40568             return false;
40569         }
40570         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40571     },
40572     
40573     
40574     
40575     // private
40576     afterRender : function(){
40577         Roo.form.Field.superclass.afterRender.call(this);
40578         this.initEvents();
40579     },
40580
40581     // private
40582     fireKey : function(e){
40583         //Roo.log('field ' + e.getKey());
40584         if(e.isNavKeyPress()){
40585             this.fireEvent("specialkey", this, e);
40586         }
40587     },
40588
40589     /**
40590      * Resets the current field value to the originally loaded value and clears any validation messages
40591      */
40592     reset : function(){
40593         this.setValue(this.resetValue);
40594         this.originalValue = this.getValue();
40595         this.clearInvalid();
40596     },
40597
40598     // private
40599     initEvents : function(){
40600         // safari killled keypress - so keydown is now used..
40601         this.el.on("keydown" , this.fireKey,  this);
40602         this.el.on("focus", this.onFocus,  this);
40603         this.el.on("blur", this.onBlur,  this);
40604         this.el.relayEvent('keyup', this);
40605
40606         // reference to original value for reset
40607         this.originalValue = this.getValue();
40608         this.resetValue =  this.getValue();
40609     },
40610
40611     // private
40612     onFocus : function(){
40613         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40614             this.el.addClass(this.focusClass);
40615         }
40616         if(!this.hasFocus){
40617             this.hasFocus = true;
40618             this.startValue = this.getValue();
40619             this.fireEvent("focus", this);
40620         }
40621     },
40622
40623     beforeBlur : Roo.emptyFn,
40624
40625     // private
40626     onBlur : function(){
40627         this.beforeBlur();
40628         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40629             this.el.removeClass(this.focusClass);
40630         }
40631         this.hasFocus = false;
40632         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40633             this.validate();
40634         }
40635         var v = this.getValue();
40636         if(String(v) !== String(this.startValue)){
40637             this.fireEvent('change', this, v, this.startValue);
40638         }
40639         this.fireEvent("blur", this);
40640     },
40641
40642     /**
40643      * Returns whether or not the field value is currently valid
40644      * @param {Boolean} preventMark True to disable marking the field invalid
40645      * @return {Boolean} True if the value is valid, else false
40646      */
40647     isValid : function(preventMark){
40648         if(this.disabled){
40649             return true;
40650         }
40651         var restore = this.preventMark;
40652         this.preventMark = preventMark === true;
40653         var v = this.validateValue(this.processValue(this.getRawValue()));
40654         this.preventMark = restore;
40655         return v;
40656     },
40657
40658     /**
40659      * Validates the field value
40660      * @return {Boolean} True if the value is valid, else false
40661      */
40662     validate : function(){
40663         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40664             this.clearInvalid();
40665             return true;
40666         }
40667         return false;
40668     },
40669
40670     processValue : function(value){
40671         return value;
40672     },
40673
40674     // private
40675     // Subclasses should provide the validation implementation by overriding this
40676     validateValue : function(value){
40677         return true;
40678     },
40679
40680     /**
40681      * Mark this field as invalid
40682      * @param {String} msg The validation message
40683      */
40684     markInvalid : function(msg){
40685         if(!this.rendered || this.preventMark){ // not rendered
40686             return;
40687         }
40688         
40689         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40690         
40691         obj.el.addClass(this.invalidClass);
40692         msg = msg || this.invalidText;
40693         switch(this.msgTarget){
40694             case 'qtip':
40695                 obj.el.dom.qtip = msg;
40696                 obj.el.dom.qclass = 'x-form-invalid-tip';
40697                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40698                     Roo.QuickTips.enable();
40699                 }
40700                 break;
40701             case 'title':
40702                 this.el.dom.title = msg;
40703                 break;
40704             case 'under':
40705                 if(!this.errorEl){
40706                     var elp = this.el.findParent('.x-form-element', 5, true);
40707                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40708                     this.errorEl.setWidth(elp.getWidth(true)-20);
40709                 }
40710                 this.errorEl.update(msg);
40711                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40712                 break;
40713             case 'side':
40714                 if(!this.errorIcon){
40715                     var elp = this.el.findParent('.x-form-element', 5, true);
40716                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40717                 }
40718                 this.alignErrorIcon();
40719                 this.errorIcon.dom.qtip = msg;
40720                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40721                 this.errorIcon.show();
40722                 this.on('resize', this.alignErrorIcon, this);
40723                 break;
40724             default:
40725                 var t = Roo.getDom(this.msgTarget);
40726                 t.innerHTML = msg;
40727                 t.style.display = this.msgDisplay;
40728                 break;
40729         }
40730         this.fireEvent('invalid', this, msg);
40731     },
40732
40733     // private
40734     alignErrorIcon : function(){
40735         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40736     },
40737
40738     /**
40739      * Clear any invalid styles/messages for this field
40740      */
40741     clearInvalid : function(){
40742         if(!this.rendered || this.preventMark){ // not rendered
40743             return;
40744         }
40745         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40746         
40747         obj.el.removeClass(this.invalidClass);
40748         switch(this.msgTarget){
40749             case 'qtip':
40750                 obj.el.dom.qtip = '';
40751                 break;
40752             case 'title':
40753                 this.el.dom.title = '';
40754                 break;
40755             case 'under':
40756                 if(this.errorEl){
40757                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40758                 }
40759                 break;
40760             case 'side':
40761                 if(this.errorIcon){
40762                     this.errorIcon.dom.qtip = '';
40763                     this.errorIcon.hide();
40764                     this.un('resize', this.alignErrorIcon, this);
40765                 }
40766                 break;
40767             default:
40768                 var t = Roo.getDom(this.msgTarget);
40769                 t.innerHTML = '';
40770                 t.style.display = 'none';
40771                 break;
40772         }
40773         this.fireEvent('valid', this);
40774     },
40775
40776     /**
40777      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40778      * @return {Mixed} value The field value
40779      */
40780     getRawValue : function(){
40781         var v = this.el.getValue();
40782         
40783         return v;
40784     },
40785
40786     /**
40787      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40788      * @return {Mixed} value The field value
40789      */
40790     getValue : function(){
40791         var v = this.el.getValue();
40792          
40793         return v;
40794     },
40795
40796     /**
40797      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40798      * @param {Mixed} value The value to set
40799      */
40800     setRawValue : function(v){
40801         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40802     },
40803
40804     /**
40805      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40806      * @param {Mixed} value The value to set
40807      */
40808     setValue : function(v){
40809         this.value = v;
40810         if(this.rendered){
40811             this.el.dom.value = (v === null || v === undefined ? '' : v);
40812              this.validate();
40813         }
40814     },
40815
40816     adjustSize : function(w, h){
40817         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40818         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40819         return s;
40820     },
40821
40822     adjustWidth : function(tag, w){
40823         tag = tag.toLowerCase();
40824         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40825             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40826                 if(tag == 'input'){
40827                     return w + 2;
40828                 }
40829                 if(tag == 'textarea'){
40830                     return w-2;
40831                 }
40832             }else if(Roo.isOpera){
40833                 if(tag == 'input'){
40834                     return w + 2;
40835                 }
40836                 if(tag == 'textarea'){
40837                     return w-2;
40838                 }
40839             }
40840         }
40841         return w;
40842     }
40843 });
40844
40845
40846 // anything other than normal should be considered experimental
40847 Roo.form.Field.msgFx = {
40848     normal : {
40849         show: function(msgEl, f){
40850             msgEl.setDisplayed('block');
40851         },
40852
40853         hide : function(msgEl, f){
40854             msgEl.setDisplayed(false).update('');
40855         }
40856     },
40857
40858     slide : {
40859         show: function(msgEl, f){
40860             msgEl.slideIn('t', {stopFx:true});
40861         },
40862
40863         hide : function(msgEl, f){
40864             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40865         }
40866     },
40867
40868     slideRight : {
40869         show: function(msgEl, f){
40870             msgEl.fixDisplay();
40871             msgEl.alignTo(f.el, 'tl-tr');
40872             msgEl.slideIn('l', {stopFx:true});
40873         },
40874
40875         hide : function(msgEl, f){
40876             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40877         }
40878     }
40879 };/*
40880  * Based on:
40881  * Ext JS Library 1.1.1
40882  * Copyright(c) 2006-2007, Ext JS, LLC.
40883  *
40884  * Originally Released Under LGPL - original licence link has changed is not relivant.
40885  *
40886  * Fork - LGPL
40887  * <script type="text/javascript">
40888  */
40889  
40890
40891 /**
40892  * @class Roo.form.TextField
40893  * @extends Roo.form.Field
40894  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40895  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40896  * @constructor
40897  * Creates a new TextField
40898  * @param {Object} config Configuration options
40899  */
40900 Roo.form.TextField = function(config){
40901     Roo.form.TextField.superclass.constructor.call(this, config);
40902     this.addEvents({
40903         /**
40904          * @event autosize
40905          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40906          * according to the default logic, but this event provides a hook for the developer to apply additional
40907          * logic at runtime to resize the field if needed.
40908              * @param {Roo.form.Field} this This text field
40909              * @param {Number} width The new field width
40910              */
40911         autosize : true
40912     });
40913 };
40914
40915 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40916     /**
40917      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40918      */
40919     grow : false,
40920     /**
40921      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40922      */
40923     growMin : 30,
40924     /**
40925      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40926      */
40927     growMax : 800,
40928     /**
40929      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40930      */
40931     vtype : null,
40932     /**
40933      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40934      */
40935     maskRe : null,
40936     /**
40937      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40938      */
40939     disableKeyFilter : false,
40940     /**
40941      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40942      */
40943     allowBlank : true,
40944     /**
40945      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40946      */
40947     minLength : 0,
40948     /**
40949      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40950      */
40951     maxLength : Number.MAX_VALUE,
40952     /**
40953      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40954      */
40955     minLengthText : "The minimum length for this field is {0}",
40956     /**
40957      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40958      */
40959     maxLengthText : "The maximum length for this field is {0}",
40960     /**
40961      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40962      */
40963     selectOnFocus : false,
40964     /**
40965      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40966      */    
40967     allowLeadingSpace : false,
40968     /**
40969      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40970      */
40971     blankText : "This field is required",
40972     /**
40973      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40974      * If available, this function will be called only after the basic validators all return true, and will be passed the
40975      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40976      */
40977     validator : null,
40978     /**
40979      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40980      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40981      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40982      */
40983     regex : null,
40984     /**
40985      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40986      */
40987     regexText : "",
40988     /**
40989      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40990      */
40991     emptyText : null,
40992    
40993
40994     // private
40995     initEvents : function()
40996     {
40997         if (this.emptyText) {
40998             this.el.attr('placeholder', this.emptyText);
40999         }
41000         
41001         Roo.form.TextField.superclass.initEvents.call(this);
41002         if(this.validationEvent == 'keyup'){
41003             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41004             this.el.on('keyup', this.filterValidation, this);
41005         }
41006         else if(this.validationEvent !== false){
41007             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41008         }
41009         
41010         if(this.selectOnFocus){
41011             this.on("focus", this.preFocus, this);
41012         }
41013         if (!this.allowLeadingSpace) {
41014             this.on('blur', this.cleanLeadingSpace, this);
41015         }
41016         
41017         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41018             this.el.on("keypress", this.filterKeys, this);
41019         }
41020         if(this.grow){
41021             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
41022             this.el.on("click", this.autoSize,  this);
41023         }
41024         if(this.el.is('input[type=password]') && Roo.isSafari){
41025             this.el.on('keydown', this.SafariOnKeyDown, this);
41026         }
41027     },
41028
41029     processValue : function(value){
41030         if(this.stripCharsRe){
41031             var newValue = value.replace(this.stripCharsRe, '');
41032             if(newValue !== value){
41033                 this.setRawValue(newValue);
41034                 return newValue;
41035             }
41036         }
41037         return value;
41038     },
41039
41040     filterValidation : function(e){
41041         if(!e.isNavKeyPress()){
41042             this.validationTask.delay(this.validationDelay);
41043         }
41044     },
41045
41046     // private
41047     onKeyUp : function(e){
41048         if(!e.isNavKeyPress()){
41049             this.autoSize();
41050         }
41051     },
41052     // private - clean the leading white space
41053     cleanLeadingSpace : function(e)
41054     {
41055         if ( this.inputType == 'file') {
41056             return;
41057         }
41058         
41059         this.setValue((this.getValue() + '').replace(/^\s+/,''));
41060     },
41061     /**
41062      * Resets the current field value to the originally-loaded value and clears any validation messages.
41063      *  
41064      */
41065     reset : function(){
41066         Roo.form.TextField.superclass.reset.call(this);
41067        
41068     }, 
41069     // private
41070     preFocus : function(){
41071         
41072         if(this.selectOnFocus){
41073             this.el.dom.select();
41074         }
41075     },
41076
41077     
41078     // private
41079     filterKeys : function(e){
41080         var k = e.getKey();
41081         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
41082             return;
41083         }
41084         var c = e.getCharCode(), cc = String.fromCharCode(c);
41085         if(Roo.isIE && (e.isSpecialKey() || !cc)){
41086             return;
41087         }
41088         if(!this.maskRe.test(cc)){
41089             e.stopEvent();
41090         }
41091     },
41092
41093     setValue : function(v){
41094         
41095         Roo.form.TextField.superclass.setValue.apply(this, arguments);
41096         
41097         this.autoSize();
41098     },
41099
41100     /**
41101      * Validates a value according to the field's validation rules and marks the field as invalid
41102      * if the validation fails
41103      * @param {Mixed} value The value to validate
41104      * @return {Boolean} True if the value is valid, else false
41105      */
41106     validateValue : function(value){
41107         if(value.length < 1)  { // if it's blank
41108              if(this.allowBlank){
41109                 this.clearInvalid();
41110                 return true;
41111              }else{
41112                 this.markInvalid(this.blankText);
41113                 return false;
41114              }
41115         }
41116         if(value.length < this.minLength){
41117             this.markInvalid(String.format(this.minLengthText, this.minLength));
41118             return false;
41119         }
41120         if(value.length > this.maxLength){
41121             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
41122             return false;
41123         }
41124         if(this.vtype){
41125             var vt = Roo.form.VTypes;
41126             if(!vt[this.vtype](value, this)){
41127                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
41128                 return false;
41129             }
41130         }
41131         if(typeof this.validator == "function"){
41132             var msg = this.validator(value);
41133             if(msg !== true){
41134                 this.markInvalid(msg);
41135                 return false;
41136             }
41137         }
41138         if(this.regex && !this.regex.test(value)){
41139             this.markInvalid(this.regexText);
41140             return false;
41141         }
41142         return true;
41143     },
41144
41145     /**
41146      * Selects text in this field
41147      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
41148      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
41149      */
41150     selectText : function(start, end){
41151         var v = this.getRawValue();
41152         if(v.length > 0){
41153             start = start === undefined ? 0 : start;
41154             end = end === undefined ? v.length : end;
41155             var d = this.el.dom;
41156             if(d.setSelectionRange){
41157                 d.setSelectionRange(start, end);
41158             }else if(d.createTextRange){
41159                 var range = d.createTextRange();
41160                 range.moveStart("character", start);
41161                 range.moveEnd("character", v.length-end);
41162                 range.select();
41163             }
41164         }
41165     },
41166
41167     /**
41168      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
41169      * This only takes effect if grow = true, and fires the autosize event.
41170      */
41171     autoSize : function(){
41172         if(!this.grow || !this.rendered){
41173             return;
41174         }
41175         if(!this.metrics){
41176             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
41177         }
41178         var el = this.el;
41179         var v = el.dom.value;
41180         var d = document.createElement('div');
41181         d.appendChild(document.createTextNode(v));
41182         v = d.innerHTML;
41183         d = null;
41184         v += "&#160;";
41185         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
41186         this.el.setWidth(w);
41187         this.fireEvent("autosize", this, w);
41188     },
41189     
41190     // private
41191     SafariOnKeyDown : function(event)
41192     {
41193         // this is a workaround for a password hang bug on chrome/ webkit.
41194         
41195         var isSelectAll = false;
41196         
41197         if(this.el.dom.selectionEnd > 0){
41198             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
41199         }
41200         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
41201             event.preventDefault();
41202             this.setValue('');
41203             return;
41204         }
41205         
41206         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
41207             
41208             event.preventDefault();
41209             // this is very hacky as keydown always get's upper case.
41210             
41211             var cc = String.fromCharCode(event.getCharCode());
41212             
41213             
41214             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
41215             
41216         }
41217         
41218         
41219     }
41220 });/*
41221  * Based on:
41222  * Ext JS Library 1.1.1
41223  * Copyright(c) 2006-2007, Ext JS, LLC.
41224  *
41225  * Originally Released Under LGPL - original licence link has changed is not relivant.
41226  *
41227  * Fork - LGPL
41228  * <script type="text/javascript">
41229  */
41230  
41231 /**
41232  * @class Roo.form.Hidden
41233  * @extends Roo.form.TextField
41234  * Simple Hidden element used on forms 
41235  * 
41236  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
41237  * 
41238  * @constructor
41239  * Creates a new Hidden form element.
41240  * @param {Object} config Configuration options
41241  */
41242
41243
41244
41245 // easy hidden field...
41246 Roo.form.Hidden = function(config){
41247     Roo.form.Hidden.superclass.constructor.call(this, config);
41248 };
41249   
41250 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
41251     fieldLabel:      '',
41252     inputType:      'hidden',
41253     width:          50,
41254     allowBlank:     true,
41255     labelSeparator: '',
41256     hidden:         true,
41257     itemCls :       'x-form-item-display-none'
41258
41259
41260 });
41261
41262
41263 /*
41264  * Based on:
41265  * Ext JS Library 1.1.1
41266  * Copyright(c) 2006-2007, Ext JS, LLC.
41267  *
41268  * Originally Released Under LGPL - original licence link has changed is not relivant.
41269  *
41270  * Fork - LGPL
41271  * <script type="text/javascript">
41272  */
41273  
41274 /**
41275  * @class Roo.form.TriggerField
41276  * @extends Roo.form.TextField
41277  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
41278  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
41279  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
41280  * for which you can provide a custom implementation.  For example:
41281  * <pre><code>
41282 var trigger = new Roo.form.TriggerField();
41283 trigger.onTriggerClick = myTriggerFn;
41284 trigger.applyTo('my-field');
41285 </code></pre>
41286  *
41287  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
41288  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
41289  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41290  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
41291  * @constructor
41292  * Create a new TriggerField.
41293  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
41294  * to the base TextField)
41295  */
41296 Roo.form.TriggerField = function(config){
41297     this.mimicing = false;
41298     Roo.form.TriggerField.superclass.constructor.call(this, config);
41299 };
41300
41301 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
41302     /**
41303      * @cfg {String} triggerClass A CSS class to apply to the trigger
41304      */
41305     /**
41306      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41307      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
41308      */
41309     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
41310     /**
41311      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
41312      */
41313     hideTrigger:false,
41314
41315     /** @cfg {Boolean} grow @hide */
41316     /** @cfg {Number} growMin @hide */
41317     /** @cfg {Number} growMax @hide */
41318
41319     /**
41320      * @hide 
41321      * @method
41322      */
41323     autoSize: Roo.emptyFn,
41324     // private
41325     monitorTab : true,
41326     // private
41327     deferHeight : true,
41328
41329     
41330     actionMode : 'wrap',
41331     // private
41332     onResize : function(w, h){
41333         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41334         if(typeof w == 'number'){
41335             var x = w - this.trigger.getWidth();
41336             this.el.setWidth(this.adjustWidth('input', x));
41337             this.trigger.setStyle('left', x+'px');
41338         }
41339     },
41340
41341     // private
41342     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41343
41344     // private
41345     getResizeEl : function(){
41346         return this.wrap;
41347     },
41348
41349     // private
41350     getPositionEl : function(){
41351         return this.wrap;
41352     },
41353
41354     // private
41355     alignErrorIcon : function(){
41356         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41357     },
41358
41359     // private
41360     onRender : function(ct, position){
41361         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41362         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41363         this.trigger = this.wrap.createChild(this.triggerConfig ||
41364                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41365         if(this.hideTrigger){
41366             this.trigger.setDisplayed(false);
41367         }
41368         this.initTrigger();
41369         if(!this.width){
41370             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41371         }
41372     },
41373
41374     // private
41375     initTrigger : function(){
41376         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41377         this.trigger.addClassOnOver('x-form-trigger-over');
41378         this.trigger.addClassOnClick('x-form-trigger-click');
41379     },
41380
41381     // private
41382     onDestroy : function(){
41383         if(this.trigger){
41384             this.trigger.removeAllListeners();
41385             this.trigger.remove();
41386         }
41387         if(this.wrap){
41388             this.wrap.remove();
41389         }
41390         Roo.form.TriggerField.superclass.onDestroy.call(this);
41391     },
41392
41393     // private
41394     onFocus : function(){
41395         Roo.form.TriggerField.superclass.onFocus.call(this);
41396         if(!this.mimicing){
41397             this.wrap.addClass('x-trigger-wrap-focus');
41398             this.mimicing = true;
41399             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41400             if(this.monitorTab){
41401                 this.el.on("keydown", this.checkTab, this);
41402             }
41403         }
41404     },
41405
41406     // private
41407     checkTab : function(e){
41408         if(e.getKey() == e.TAB){
41409             this.triggerBlur();
41410         }
41411     },
41412
41413     // private
41414     onBlur : function(){
41415         // do nothing
41416     },
41417
41418     // private
41419     mimicBlur : function(e, t){
41420         if(!this.wrap.contains(t) && this.validateBlur()){
41421             this.triggerBlur();
41422         }
41423     },
41424
41425     // private
41426     triggerBlur : function(){
41427         this.mimicing = false;
41428         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41429         if(this.monitorTab){
41430             this.el.un("keydown", this.checkTab, this);
41431         }
41432         this.wrap.removeClass('x-trigger-wrap-focus');
41433         Roo.form.TriggerField.superclass.onBlur.call(this);
41434     },
41435
41436     // private
41437     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41438     validateBlur : function(e, t){
41439         return true;
41440     },
41441
41442     // private
41443     onDisable : function(){
41444         Roo.form.TriggerField.superclass.onDisable.call(this);
41445         if(this.wrap){
41446             this.wrap.addClass('x-item-disabled');
41447         }
41448     },
41449
41450     // private
41451     onEnable : function(){
41452         Roo.form.TriggerField.superclass.onEnable.call(this);
41453         if(this.wrap){
41454             this.wrap.removeClass('x-item-disabled');
41455         }
41456     },
41457
41458     // private
41459     onShow : function(){
41460         var ae = this.getActionEl();
41461         
41462         if(ae){
41463             ae.dom.style.display = '';
41464             ae.dom.style.visibility = 'visible';
41465         }
41466     },
41467
41468     // private
41469     
41470     onHide : function(){
41471         var ae = this.getActionEl();
41472         ae.dom.style.display = 'none';
41473     },
41474
41475     /**
41476      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41477      * by an implementing function.
41478      * @method
41479      * @param {EventObject} e
41480      */
41481     onTriggerClick : Roo.emptyFn
41482 });
41483
41484 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41485 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41486 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41487 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41488     initComponent : function(){
41489         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41490
41491         this.triggerConfig = {
41492             tag:'span', cls:'x-form-twin-triggers', cn:[
41493             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41494             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41495         ]};
41496     },
41497
41498     getTrigger : function(index){
41499         return this.triggers[index];
41500     },
41501
41502     initTrigger : function(){
41503         var ts = this.trigger.select('.x-form-trigger', true);
41504         this.wrap.setStyle('overflow', 'hidden');
41505         var triggerField = this;
41506         ts.each(function(t, all, index){
41507             t.hide = function(){
41508                 var w = triggerField.wrap.getWidth();
41509                 this.dom.style.display = 'none';
41510                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41511             };
41512             t.show = function(){
41513                 var w = triggerField.wrap.getWidth();
41514                 this.dom.style.display = '';
41515                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41516             };
41517             var triggerIndex = 'Trigger'+(index+1);
41518
41519             if(this['hide'+triggerIndex]){
41520                 t.dom.style.display = 'none';
41521             }
41522             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41523             t.addClassOnOver('x-form-trigger-over');
41524             t.addClassOnClick('x-form-trigger-click');
41525         }, this);
41526         this.triggers = ts.elements;
41527     },
41528
41529     onTrigger1Click : Roo.emptyFn,
41530     onTrigger2Click : Roo.emptyFn
41531 });/*
41532  * Based on:
41533  * Ext JS Library 1.1.1
41534  * Copyright(c) 2006-2007, Ext JS, LLC.
41535  *
41536  * Originally Released Under LGPL - original licence link has changed is not relivant.
41537  *
41538  * Fork - LGPL
41539  * <script type="text/javascript">
41540  */
41541  
41542 /**
41543  * @class Roo.form.TextArea
41544  * @extends Roo.form.TextField
41545  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41546  * support for auto-sizing.
41547  * @constructor
41548  * Creates a new TextArea
41549  * @param {Object} config Configuration options
41550  */
41551 Roo.form.TextArea = function(config){
41552     Roo.form.TextArea.superclass.constructor.call(this, config);
41553     // these are provided exchanges for backwards compat
41554     // minHeight/maxHeight were replaced by growMin/growMax to be
41555     // compatible with TextField growing config values
41556     if(this.minHeight !== undefined){
41557         this.growMin = this.minHeight;
41558     }
41559     if(this.maxHeight !== undefined){
41560         this.growMax = this.maxHeight;
41561     }
41562 };
41563
41564 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41565     /**
41566      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41567      */
41568     growMin : 60,
41569     /**
41570      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41571      */
41572     growMax: 1000,
41573     /**
41574      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41575      * in the field (equivalent to setting overflow: hidden, defaults to false)
41576      */
41577     preventScrollbars: false,
41578     /**
41579      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41580      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41581      */
41582
41583     // private
41584     onRender : function(ct, position){
41585         if(!this.el){
41586             this.defaultAutoCreate = {
41587                 tag: "textarea",
41588                 style:"width:300px;height:60px;",
41589                 autocomplete: "new-password"
41590             };
41591         }
41592         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41593         if(this.grow){
41594             this.textSizeEl = Roo.DomHelper.append(document.body, {
41595                 tag: "pre", cls: "x-form-grow-sizer"
41596             });
41597             if(this.preventScrollbars){
41598                 this.el.setStyle("overflow", "hidden");
41599             }
41600             this.el.setHeight(this.growMin);
41601         }
41602     },
41603
41604     onDestroy : function(){
41605         if(this.textSizeEl){
41606             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41607         }
41608         Roo.form.TextArea.superclass.onDestroy.call(this);
41609     },
41610
41611     // private
41612     onKeyUp : function(e){
41613         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41614             this.autoSize();
41615         }
41616     },
41617
41618     /**
41619      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41620      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41621      */
41622     autoSize : function(){
41623         if(!this.grow || !this.textSizeEl){
41624             return;
41625         }
41626         var el = this.el;
41627         var v = el.dom.value;
41628         var ts = this.textSizeEl;
41629
41630         ts.innerHTML = '';
41631         ts.appendChild(document.createTextNode(v));
41632         v = ts.innerHTML;
41633
41634         Roo.fly(ts).setWidth(this.el.getWidth());
41635         if(v.length < 1){
41636             v = "&#160;&#160;";
41637         }else{
41638             if(Roo.isIE){
41639                 v = v.replace(/\n/g, '<p>&#160;</p>');
41640             }
41641             v += "&#160;\n&#160;";
41642         }
41643         ts.innerHTML = v;
41644         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41645         if(h != this.lastHeight){
41646             this.lastHeight = h;
41647             this.el.setHeight(h);
41648             this.fireEvent("autosize", this, h);
41649         }
41650     }
41651 });/*
41652  * Based on:
41653  * Ext JS Library 1.1.1
41654  * Copyright(c) 2006-2007, Ext JS, LLC.
41655  *
41656  * Originally Released Under LGPL - original licence link has changed is not relivant.
41657  *
41658  * Fork - LGPL
41659  * <script type="text/javascript">
41660  */
41661  
41662
41663 /**
41664  * @class Roo.form.NumberField
41665  * @extends Roo.form.TextField
41666  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41667  * @constructor
41668  * Creates a new NumberField
41669  * @param {Object} config Configuration options
41670  */
41671 Roo.form.NumberField = function(config){
41672     Roo.form.NumberField.superclass.constructor.call(this, config);
41673 };
41674
41675 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41676     /**
41677      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41678      */
41679     fieldClass: "x-form-field x-form-num-field",
41680     /**
41681      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41682      */
41683     allowDecimals : true,
41684     /**
41685      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41686      */
41687     decimalSeparator : ".",
41688     /**
41689      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41690      */
41691     decimalPrecision : 2,
41692     /**
41693      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41694      */
41695     allowNegative : true,
41696     /**
41697      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41698      */
41699     minValue : Number.NEGATIVE_INFINITY,
41700     /**
41701      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41702      */
41703     maxValue : Number.MAX_VALUE,
41704     /**
41705      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41706      */
41707     minText : "The minimum value for this field is {0}",
41708     /**
41709      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41710      */
41711     maxText : "The maximum value for this field is {0}",
41712     /**
41713      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41714      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41715      */
41716     nanText : "{0} is not a valid number",
41717
41718     // private
41719     initEvents : function(){
41720         Roo.form.NumberField.superclass.initEvents.call(this);
41721         var allowed = "0123456789";
41722         if(this.allowDecimals){
41723             allowed += this.decimalSeparator;
41724         }
41725         if(this.allowNegative){
41726             allowed += "-";
41727         }
41728         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41729         var keyPress = function(e){
41730             var k = e.getKey();
41731             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41732                 return;
41733             }
41734             var c = e.getCharCode();
41735             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41736                 e.stopEvent();
41737             }
41738         };
41739         this.el.on("keypress", keyPress, this);
41740     },
41741
41742     // private
41743     validateValue : function(value){
41744         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41745             return false;
41746         }
41747         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41748              return true;
41749         }
41750         var num = this.parseValue(value);
41751         if(isNaN(num)){
41752             this.markInvalid(String.format(this.nanText, value));
41753             return false;
41754         }
41755         if(num < this.minValue){
41756             this.markInvalid(String.format(this.minText, this.minValue));
41757             return false;
41758         }
41759         if(num > this.maxValue){
41760             this.markInvalid(String.format(this.maxText, this.maxValue));
41761             return false;
41762         }
41763         return true;
41764     },
41765
41766     getValue : function(){
41767         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41768     },
41769
41770     // private
41771     parseValue : function(value){
41772         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41773         return isNaN(value) ? '' : value;
41774     },
41775
41776     // private
41777     fixPrecision : function(value){
41778         var nan = isNaN(value);
41779         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41780             return nan ? '' : value;
41781         }
41782         return parseFloat(value).toFixed(this.decimalPrecision);
41783     },
41784
41785     setValue : function(v){
41786         v = this.fixPrecision(v);
41787         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41788     },
41789
41790     // private
41791     decimalPrecisionFcn : function(v){
41792         return Math.floor(v);
41793     },
41794
41795     beforeBlur : function(){
41796         var v = this.parseValue(this.getRawValue());
41797         if(v){
41798             this.setValue(v);
41799         }
41800     }
41801 });/*
41802  * Based on:
41803  * Ext JS Library 1.1.1
41804  * Copyright(c) 2006-2007, Ext JS, LLC.
41805  *
41806  * Originally Released Under LGPL - original licence link has changed is not relivant.
41807  *
41808  * Fork - LGPL
41809  * <script type="text/javascript">
41810  */
41811  
41812 /**
41813  * @class Roo.form.DateField
41814  * @extends Roo.form.TriggerField
41815  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41816 * @constructor
41817 * Create a new DateField
41818 * @param {Object} config
41819  */
41820 Roo.form.DateField = function(config)
41821 {
41822     Roo.form.DateField.superclass.constructor.call(this, config);
41823     
41824       this.addEvents({
41825          
41826         /**
41827          * @event select
41828          * Fires when a date is selected
41829              * @param {Roo.form.DateField} combo This combo box
41830              * @param {Date} date The date selected
41831              */
41832         'select' : true
41833          
41834     });
41835     
41836     
41837     if(typeof this.minValue == "string") {
41838         this.minValue = this.parseDate(this.minValue);
41839     }
41840     if(typeof this.maxValue == "string") {
41841         this.maxValue = this.parseDate(this.maxValue);
41842     }
41843     this.ddMatch = null;
41844     if(this.disabledDates){
41845         var dd = this.disabledDates;
41846         var re = "(?:";
41847         for(var i = 0; i < dd.length; i++){
41848             re += dd[i];
41849             if(i != dd.length-1) {
41850                 re += "|";
41851             }
41852         }
41853         this.ddMatch = new RegExp(re + ")");
41854     }
41855 };
41856
41857 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41858     /**
41859      * @cfg {String} format
41860      * The default date format string which can be overriden for localization support.  The format must be
41861      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41862      */
41863     format : "m/d/y",
41864     /**
41865      * @cfg {String} altFormats
41866      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41867      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41868      */
41869     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41870     /**
41871      * @cfg {Array} disabledDays
41872      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41873      */
41874     disabledDays : null,
41875     /**
41876      * @cfg {String} disabledDaysText
41877      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41878      */
41879     disabledDaysText : "Disabled",
41880     /**
41881      * @cfg {Array} disabledDates
41882      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41883      * expression so they are very powerful. Some examples:
41884      * <ul>
41885      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41886      * <li>["03/08", "09/16"] would disable those days for every year</li>
41887      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41888      * <li>["03/../2006"] would disable every day in March 2006</li>
41889      * <li>["^03"] would disable every day in every March</li>
41890      * </ul>
41891      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41892      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41893      */
41894     disabledDates : null,
41895     /**
41896      * @cfg {String} disabledDatesText
41897      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41898      */
41899     disabledDatesText : "Disabled",
41900         
41901         
41902         /**
41903      * @cfg {Date/String} zeroValue
41904      * if the date is less that this number, then the field is rendered as empty
41905      * default is 1800
41906      */
41907         zeroValue : '1800-01-01',
41908         
41909         
41910     /**
41911      * @cfg {Date/String} minValue
41912      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41913      * valid format (defaults to null).
41914      */
41915     minValue : null,
41916     /**
41917      * @cfg {Date/String} maxValue
41918      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41919      * valid format (defaults to null).
41920      */
41921     maxValue : null,
41922     /**
41923      * @cfg {String} minText
41924      * The error text to display when the date in the cell is before minValue (defaults to
41925      * 'The date in this field must be after {minValue}').
41926      */
41927     minText : "The date in this field must be equal to or after {0}",
41928     /**
41929      * @cfg {String} maxText
41930      * The error text to display when the date in the cell is after maxValue (defaults to
41931      * 'The date in this field must be before {maxValue}').
41932      */
41933     maxText : "The date in this field must be equal to or before {0}",
41934     /**
41935      * @cfg {String} invalidText
41936      * The error text to display when the date in the field is invalid (defaults to
41937      * '{value} is not a valid date - it must be in the format {format}').
41938      */
41939     invalidText : "{0} is not a valid date - it must be in the format {1}",
41940     /**
41941      * @cfg {String} triggerClass
41942      * An additional CSS class used to style the trigger button.  The trigger will always get the
41943      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41944      * which displays a calendar icon).
41945      */
41946     triggerClass : 'x-form-date-trigger',
41947     
41948
41949     /**
41950      * @cfg {Boolean} useIso
41951      * if enabled, then the date field will use a hidden field to store the 
41952      * real value as iso formated date. default (false)
41953      */ 
41954     useIso : false,
41955     /**
41956      * @cfg {String/Object} autoCreate
41957      * A DomHelper element spec, or true for a default element spec (defaults to
41958      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41959      */ 
41960     // private
41961     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41962     
41963     // private
41964     hiddenField: false,
41965     
41966     onRender : function(ct, position)
41967     {
41968         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41969         if (this.useIso) {
41970             //this.el.dom.removeAttribute('name'); 
41971             Roo.log("Changing name?");
41972             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41973             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41974                     'before', true);
41975             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41976             // prevent input submission
41977             this.hiddenName = this.name;
41978         }
41979             
41980             
41981     },
41982     
41983     // private
41984     validateValue : function(value)
41985     {
41986         value = this.formatDate(value);
41987         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41988             Roo.log('super failed');
41989             return false;
41990         }
41991         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41992              return true;
41993         }
41994         var svalue = value;
41995         value = this.parseDate(value);
41996         if(!value){
41997             Roo.log('parse date failed' + svalue);
41998             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41999             return false;
42000         }
42001         var time = value.getTime();
42002         if(this.minValue && time < this.minValue.getTime()){
42003             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42004             return false;
42005         }
42006         if(this.maxValue && time > this.maxValue.getTime()){
42007             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42008             return false;
42009         }
42010         if(this.disabledDays){
42011             var day = value.getDay();
42012             for(var i = 0; i < this.disabledDays.length; i++) {
42013                 if(day === this.disabledDays[i]){
42014                     this.markInvalid(this.disabledDaysText);
42015                     return false;
42016                 }
42017             }
42018         }
42019         var fvalue = this.formatDate(value);
42020         if(this.ddMatch && this.ddMatch.test(fvalue)){
42021             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42022             return false;
42023         }
42024         return true;
42025     },
42026
42027     // private
42028     // Provides logic to override the default TriggerField.validateBlur which just returns true
42029     validateBlur : function(){
42030         return !this.menu || !this.menu.isVisible();
42031     },
42032     
42033     getName: function()
42034     {
42035         // returns hidden if it's set..
42036         if (!this.rendered) {return ''};
42037         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42038         
42039     },
42040
42041     /**
42042      * Returns the current date value of the date field.
42043      * @return {Date} The date value
42044      */
42045     getValue : function(){
42046         
42047         return  this.hiddenField ?
42048                 this.hiddenField.value :
42049                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
42050     },
42051
42052     /**
42053      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42054      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
42055      * (the default format used is "m/d/y").
42056      * <br />Usage:
42057      * <pre><code>
42058 //All of these calls set the same date value (May 4, 2006)
42059
42060 //Pass a date object:
42061 var dt = new Date('5/4/06');
42062 dateField.setValue(dt);
42063
42064 //Pass a date string (default format):
42065 dateField.setValue('5/4/06');
42066
42067 //Pass a date string (custom format):
42068 dateField.format = 'Y-m-d';
42069 dateField.setValue('2006-5-4');
42070 </code></pre>
42071      * @param {String/Date} date The date or valid date string
42072      */
42073     setValue : function(date){
42074         if (this.hiddenField) {
42075             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42076         }
42077         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42078         // make sure the value field is always stored as a date..
42079         this.value = this.parseDate(date);
42080         
42081         
42082     },
42083
42084     // private
42085     parseDate : function(value){
42086                 
42087                 if (value instanceof Date) {
42088                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42089                                 return  '';
42090                         }
42091                         return value;
42092                 }
42093                 
42094                 
42095         if(!value || value instanceof Date){
42096             return value;
42097         }
42098         var v = Date.parseDate(value, this.format);
42099          if (!v && this.useIso) {
42100             v = Date.parseDate(value, 'Y-m-d');
42101         }
42102         if(!v && this.altFormats){
42103             if(!this.altFormatsArray){
42104                 this.altFormatsArray = this.altFormats.split("|");
42105             }
42106             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42107                 v = Date.parseDate(value, this.altFormatsArray[i]);
42108             }
42109         }
42110                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42111                         v = '';
42112                 }
42113         return v;
42114     },
42115
42116     // private
42117     formatDate : function(date, fmt){
42118         return (!date || !(date instanceof Date)) ?
42119                date : date.dateFormat(fmt || this.format);
42120     },
42121
42122     // private
42123     menuListeners : {
42124         select: function(m, d){
42125             
42126             this.setValue(d);
42127             this.fireEvent('select', this, d);
42128         },
42129         show : function(){ // retain focus styling
42130             this.onFocus();
42131         },
42132         hide : function(){
42133             this.focus.defer(10, this);
42134             var ml = this.menuListeners;
42135             this.menu.un("select", ml.select,  this);
42136             this.menu.un("show", ml.show,  this);
42137             this.menu.un("hide", ml.hide,  this);
42138         }
42139     },
42140
42141     // private
42142     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42143     onTriggerClick : function(){
42144         if(this.disabled){
42145             return;
42146         }
42147         if(this.menu == null){
42148             this.menu = new Roo.menu.DateMenu();
42149         }
42150         Roo.apply(this.menu.picker,  {
42151             showClear: this.allowBlank,
42152             minDate : this.minValue,
42153             maxDate : this.maxValue,
42154             disabledDatesRE : this.ddMatch,
42155             disabledDatesText : this.disabledDatesText,
42156             disabledDays : this.disabledDays,
42157             disabledDaysText : this.disabledDaysText,
42158             format : this.useIso ? 'Y-m-d' : this.format,
42159             minText : String.format(this.minText, this.formatDate(this.minValue)),
42160             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42161         });
42162         this.menu.on(Roo.apply({}, this.menuListeners, {
42163             scope:this
42164         }));
42165         this.menu.picker.setValue(this.getValue() || new Date());
42166         this.menu.show(this.el, "tl-bl?");
42167     },
42168
42169     beforeBlur : function(){
42170         var v = this.parseDate(this.getRawValue());
42171         if(v){
42172             this.setValue(v);
42173         }
42174     },
42175
42176     /*@
42177      * overide
42178      * 
42179      */
42180     isDirty : function() {
42181         if(this.disabled) {
42182             return false;
42183         }
42184         
42185         if(typeof(this.startValue) === 'undefined'){
42186             return false;
42187         }
42188         
42189         return String(this.getValue()) !== String(this.startValue);
42190         
42191     },
42192     // @overide
42193     cleanLeadingSpace : function(e)
42194     {
42195        return;
42196     }
42197     
42198 });/*
42199  * Based on:
42200  * Ext JS Library 1.1.1
42201  * Copyright(c) 2006-2007, Ext JS, LLC.
42202  *
42203  * Originally Released Under LGPL - original licence link has changed is not relivant.
42204  *
42205  * Fork - LGPL
42206  * <script type="text/javascript">
42207  */
42208  
42209 /**
42210  * @class Roo.form.MonthField
42211  * @extends Roo.form.TriggerField
42212  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42213 * @constructor
42214 * Create a new MonthField
42215 * @param {Object} config
42216  */
42217 Roo.form.MonthField = function(config){
42218     
42219     Roo.form.MonthField.superclass.constructor.call(this, config);
42220     
42221       this.addEvents({
42222          
42223         /**
42224          * @event select
42225          * Fires when a date is selected
42226              * @param {Roo.form.MonthFieeld} combo This combo box
42227              * @param {Date} date The date selected
42228              */
42229         'select' : true
42230          
42231     });
42232     
42233     
42234     if(typeof this.minValue == "string") {
42235         this.minValue = this.parseDate(this.minValue);
42236     }
42237     if(typeof this.maxValue == "string") {
42238         this.maxValue = this.parseDate(this.maxValue);
42239     }
42240     this.ddMatch = null;
42241     if(this.disabledDates){
42242         var dd = this.disabledDates;
42243         var re = "(?:";
42244         for(var i = 0; i < dd.length; i++){
42245             re += dd[i];
42246             if(i != dd.length-1) {
42247                 re += "|";
42248             }
42249         }
42250         this.ddMatch = new RegExp(re + ")");
42251     }
42252 };
42253
42254 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
42255     /**
42256      * @cfg {String} format
42257      * The default date format string which can be overriden for localization support.  The format must be
42258      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42259      */
42260     format : "M Y",
42261     /**
42262      * @cfg {String} altFormats
42263      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42264      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42265      */
42266     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
42267     /**
42268      * @cfg {Array} disabledDays
42269      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42270      */
42271     disabledDays : [0,1,2,3,4,5,6],
42272     /**
42273      * @cfg {String} disabledDaysText
42274      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42275      */
42276     disabledDaysText : "Disabled",
42277     /**
42278      * @cfg {Array} disabledDates
42279      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42280      * expression so they are very powerful. Some examples:
42281      * <ul>
42282      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42283      * <li>["03/08", "09/16"] would disable those days for every year</li>
42284      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42285      * <li>["03/../2006"] would disable every day in March 2006</li>
42286      * <li>["^03"] would disable every day in every March</li>
42287      * </ul>
42288      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42289      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42290      */
42291     disabledDates : null,
42292     /**
42293      * @cfg {String} disabledDatesText
42294      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42295      */
42296     disabledDatesText : "Disabled",
42297     /**
42298      * @cfg {Date/String} minValue
42299      * The minimum allowed date. Can be either a Javascript date object or a string date in a
42300      * valid format (defaults to null).
42301      */
42302     minValue : null,
42303     /**
42304      * @cfg {Date/String} maxValue
42305      * The maximum allowed date. Can be either a Javascript date object or a string date in a
42306      * valid format (defaults to null).
42307      */
42308     maxValue : null,
42309     /**
42310      * @cfg {String} minText
42311      * The error text to display when the date in the cell is before minValue (defaults to
42312      * 'The date in this field must be after {minValue}').
42313      */
42314     minText : "The date in this field must be equal to or after {0}",
42315     /**
42316      * @cfg {String} maxTextf
42317      * The error text to display when the date in the cell is after maxValue (defaults to
42318      * 'The date in this field must be before {maxValue}').
42319      */
42320     maxText : "The date in this field must be equal to or before {0}",
42321     /**
42322      * @cfg {String} invalidText
42323      * The error text to display when the date in the field is invalid (defaults to
42324      * '{value} is not a valid date - it must be in the format {format}').
42325      */
42326     invalidText : "{0} is not a valid date - it must be in the format {1}",
42327     /**
42328      * @cfg {String} triggerClass
42329      * An additional CSS class used to style the trigger button.  The trigger will always get the
42330      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42331      * which displays a calendar icon).
42332      */
42333     triggerClass : 'x-form-date-trigger',
42334     
42335
42336     /**
42337      * @cfg {Boolean} useIso
42338      * if enabled, then the date field will use a hidden field to store the 
42339      * real value as iso formated date. default (true)
42340      */ 
42341     useIso : true,
42342     /**
42343      * @cfg {String/Object} autoCreate
42344      * A DomHelper element spec, or true for a default element spec (defaults to
42345      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42346      */ 
42347     // private
42348     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42349     
42350     // private
42351     hiddenField: false,
42352     
42353     hideMonthPicker : false,
42354     
42355     onRender : function(ct, position)
42356     {
42357         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42358         if (this.useIso) {
42359             this.el.dom.removeAttribute('name'); 
42360             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42361                     'before', true);
42362             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42363             // prevent input submission
42364             this.hiddenName = this.name;
42365         }
42366             
42367             
42368     },
42369     
42370     // private
42371     validateValue : function(value)
42372     {
42373         value = this.formatDate(value);
42374         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42375             return false;
42376         }
42377         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42378              return true;
42379         }
42380         var svalue = value;
42381         value = this.parseDate(value);
42382         if(!value){
42383             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42384             return false;
42385         }
42386         var time = value.getTime();
42387         if(this.minValue && time < this.minValue.getTime()){
42388             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42389             return false;
42390         }
42391         if(this.maxValue && time > this.maxValue.getTime()){
42392             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42393             return false;
42394         }
42395         /*if(this.disabledDays){
42396             var day = value.getDay();
42397             for(var i = 0; i < this.disabledDays.length; i++) {
42398                 if(day === this.disabledDays[i]){
42399                     this.markInvalid(this.disabledDaysText);
42400                     return false;
42401                 }
42402             }
42403         }
42404         */
42405         var fvalue = this.formatDate(value);
42406         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42407             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42408             return false;
42409         }
42410         */
42411         return true;
42412     },
42413
42414     // private
42415     // Provides logic to override the default TriggerField.validateBlur which just returns true
42416     validateBlur : function(){
42417         return !this.menu || !this.menu.isVisible();
42418     },
42419
42420     /**
42421      * Returns the current date value of the date field.
42422      * @return {Date} The date value
42423      */
42424     getValue : function(){
42425         
42426         
42427         
42428         return  this.hiddenField ?
42429                 this.hiddenField.value :
42430                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42431     },
42432
42433     /**
42434      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42435      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42436      * (the default format used is "m/d/y").
42437      * <br />Usage:
42438      * <pre><code>
42439 //All of these calls set the same date value (May 4, 2006)
42440
42441 //Pass a date object:
42442 var dt = new Date('5/4/06');
42443 monthField.setValue(dt);
42444
42445 //Pass a date string (default format):
42446 monthField.setValue('5/4/06');
42447
42448 //Pass a date string (custom format):
42449 monthField.format = 'Y-m-d';
42450 monthField.setValue('2006-5-4');
42451 </code></pre>
42452      * @param {String/Date} date The date or valid date string
42453      */
42454     setValue : function(date){
42455         Roo.log('month setValue' + date);
42456         // can only be first of month..
42457         
42458         var val = this.parseDate(date);
42459         
42460         if (this.hiddenField) {
42461             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42462         }
42463         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42464         this.value = this.parseDate(date);
42465     },
42466
42467     // private
42468     parseDate : function(value){
42469         if(!value || value instanceof Date){
42470             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42471             return value;
42472         }
42473         var v = Date.parseDate(value, this.format);
42474         if (!v && this.useIso) {
42475             v = Date.parseDate(value, 'Y-m-d');
42476         }
42477         if (v) {
42478             // 
42479             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42480         }
42481         
42482         
42483         if(!v && this.altFormats){
42484             if(!this.altFormatsArray){
42485                 this.altFormatsArray = this.altFormats.split("|");
42486             }
42487             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42488                 v = Date.parseDate(value, this.altFormatsArray[i]);
42489             }
42490         }
42491         return v;
42492     },
42493
42494     // private
42495     formatDate : function(date, fmt){
42496         return (!date || !(date instanceof Date)) ?
42497                date : date.dateFormat(fmt || this.format);
42498     },
42499
42500     // private
42501     menuListeners : {
42502         select: function(m, d){
42503             this.setValue(d);
42504             this.fireEvent('select', this, d);
42505         },
42506         show : function(){ // retain focus styling
42507             this.onFocus();
42508         },
42509         hide : function(){
42510             this.focus.defer(10, this);
42511             var ml = this.menuListeners;
42512             this.menu.un("select", ml.select,  this);
42513             this.menu.un("show", ml.show,  this);
42514             this.menu.un("hide", ml.hide,  this);
42515         }
42516     },
42517     // private
42518     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42519     onTriggerClick : function(){
42520         if(this.disabled){
42521             return;
42522         }
42523         if(this.menu == null){
42524             this.menu = new Roo.menu.DateMenu();
42525            
42526         }
42527         
42528         Roo.apply(this.menu.picker,  {
42529             
42530             showClear: this.allowBlank,
42531             minDate : this.minValue,
42532             maxDate : this.maxValue,
42533             disabledDatesRE : this.ddMatch,
42534             disabledDatesText : this.disabledDatesText,
42535             
42536             format : this.useIso ? 'Y-m-d' : this.format,
42537             minText : String.format(this.minText, this.formatDate(this.minValue)),
42538             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42539             
42540         });
42541          this.menu.on(Roo.apply({}, this.menuListeners, {
42542             scope:this
42543         }));
42544        
42545         
42546         var m = this.menu;
42547         var p = m.picker;
42548         
42549         // hide month picker get's called when we called by 'before hide';
42550         
42551         var ignorehide = true;
42552         p.hideMonthPicker  = function(disableAnim){
42553             if (ignorehide) {
42554                 return;
42555             }
42556              if(this.monthPicker){
42557                 Roo.log("hideMonthPicker called");
42558                 if(disableAnim === true){
42559                     this.monthPicker.hide();
42560                 }else{
42561                     this.monthPicker.slideOut('t', {duration:.2});
42562                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42563                     p.fireEvent("select", this, this.value);
42564                     m.hide();
42565                 }
42566             }
42567         }
42568         
42569         Roo.log('picker set value');
42570         Roo.log(this.getValue());
42571         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42572         m.show(this.el, 'tl-bl?');
42573         ignorehide  = false;
42574         // this will trigger hideMonthPicker..
42575         
42576         
42577         // hidden the day picker
42578         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42579         
42580         
42581         
42582       
42583         
42584         p.showMonthPicker.defer(100, p);
42585     
42586         
42587        
42588     },
42589
42590     beforeBlur : function(){
42591         var v = this.parseDate(this.getRawValue());
42592         if(v){
42593             this.setValue(v);
42594         }
42595     }
42596
42597     /** @cfg {Boolean} grow @hide */
42598     /** @cfg {Number} growMin @hide */
42599     /** @cfg {Number} growMax @hide */
42600     /**
42601      * @hide
42602      * @method autoSize
42603      */
42604 });/*
42605  * Based on:
42606  * Ext JS Library 1.1.1
42607  * Copyright(c) 2006-2007, Ext JS, LLC.
42608  *
42609  * Originally Released Under LGPL - original licence link has changed is not relivant.
42610  *
42611  * Fork - LGPL
42612  * <script type="text/javascript">
42613  */
42614  
42615
42616 /**
42617  * @class Roo.form.ComboBox
42618  * @extends Roo.form.TriggerField
42619  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42620  * @constructor
42621  * Create a new ComboBox.
42622  * @param {Object} config Configuration options
42623  */
42624 Roo.form.ComboBox = function(config){
42625     Roo.form.ComboBox.superclass.constructor.call(this, config);
42626     this.addEvents({
42627         /**
42628          * @event expand
42629          * Fires when the dropdown list is expanded
42630              * @param {Roo.form.ComboBox} combo This combo box
42631              */
42632         'expand' : true,
42633         /**
42634          * @event collapse
42635          * Fires when the dropdown list is collapsed
42636              * @param {Roo.form.ComboBox} combo This combo box
42637              */
42638         'collapse' : true,
42639         /**
42640          * @event beforeselect
42641          * Fires before a list item is selected. Return false to cancel the selection.
42642              * @param {Roo.form.ComboBox} combo This combo box
42643              * @param {Roo.data.Record} record The data record returned from the underlying store
42644              * @param {Number} index The index of the selected item in the dropdown list
42645              */
42646         'beforeselect' : true,
42647         /**
42648          * @event select
42649          * Fires when a list item is selected
42650              * @param {Roo.form.ComboBox} combo This combo box
42651              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42652              * @param {Number} index The index of the selected item in the dropdown list
42653              */
42654         'select' : true,
42655         /**
42656          * @event beforequery
42657          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42658          * The event object passed has these properties:
42659              * @param {Roo.form.ComboBox} combo This combo box
42660              * @param {String} query The query
42661              * @param {Boolean} forceAll true to force "all" query
42662              * @param {Boolean} cancel true to cancel the query
42663              * @param {Object} e The query event object
42664              */
42665         'beforequery': true,
42666          /**
42667          * @event add
42668          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42669              * @param {Roo.form.ComboBox} combo This combo box
42670              */
42671         'add' : true,
42672         /**
42673          * @event edit
42674          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42675              * @param {Roo.form.ComboBox} combo This combo box
42676              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42677              */
42678         'edit' : true
42679         
42680         
42681     });
42682     if(this.transform){
42683         this.allowDomMove = false;
42684         var s = Roo.getDom(this.transform);
42685         if(!this.hiddenName){
42686             this.hiddenName = s.name;
42687         }
42688         if(!this.store){
42689             this.mode = 'local';
42690             var d = [], opts = s.options;
42691             for(var i = 0, len = opts.length;i < len; i++){
42692                 var o = opts[i];
42693                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42694                 if(o.selected) {
42695                     this.value = value;
42696                 }
42697                 d.push([value, o.text]);
42698             }
42699             this.store = new Roo.data.SimpleStore({
42700                 'id': 0,
42701                 fields: ['value', 'text'],
42702                 data : d
42703             });
42704             this.valueField = 'value';
42705             this.displayField = 'text';
42706         }
42707         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42708         if(!this.lazyRender){
42709             this.target = true;
42710             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42711             s.parentNode.removeChild(s); // remove it
42712             this.render(this.el.parentNode);
42713         }else{
42714             s.parentNode.removeChild(s); // remove it
42715         }
42716
42717     }
42718     if (this.store) {
42719         this.store = Roo.factory(this.store, Roo.data);
42720     }
42721     
42722     this.selectedIndex = -1;
42723     if(this.mode == 'local'){
42724         if(config.queryDelay === undefined){
42725             this.queryDelay = 10;
42726         }
42727         if(config.minChars === undefined){
42728             this.minChars = 0;
42729         }
42730     }
42731 };
42732
42733 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42734     /**
42735      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42736      */
42737     /**
42738      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42739      * rendering into an Roo.Editor, defaults to false)
42740      */
42741     /**
42742      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42743      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42744      */
42745     /**
42746      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42747      */
42748     /**
42749      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42750      * the dropdown list (defaults to undefined, with no header element)
42751      */
42752
42753      /**
42754      * @cfg {String/Roo.Template} tpl The template to use to render the output
42755      */
42756      
42757     // private
42758     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42759     /**
42760      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42761      */
42762     listWidth: undefined,
42763     /**
42764      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42765      * mode = 'remote' or 'text' if mode = 'local')
42766      */
42767     displayField: undefined,
42768     /**
42769      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42770      * mode = 'remote' or 'value' if mode = 'local'). 
42771      * Note: use of a valueField requires the user make a selection
42772      * in order for a value to be mapped.
42773      */
42774     valueField: undefined,
42775     
42776     
42777     /**
42778      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42779      * field's data value (defaults to the underlying DOM element's name)
42780      */
42781     hiddenName: undefined,
42782     /**
42783      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42784      */
42785     listClass: '',
42786     /**
42787      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42788      */
42789     selectedClass: 'x-combo-selected',
42790     /**
42791      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42792      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42793      * which displays a downward arrow icon).
42794      */
42795     triggerClass : 'x-form-arrow-trigger',
42796     /**
42797      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42798      */
42799     shadow:'sides',
42800     /**
42801      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42802      * anchor positions (defaults to 'tl-bl')
42803      */
42804     listAlign: 'tl-bl?',
42805     /**
42806      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42807      */
42808     maxHeight: 300,
42809     /**
42810      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42811      * query specified by the allQuery config option (defaults to 'query')
42812      */
42813     triggerAction: 'query',
42814     /**
42815      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42816      * (defaults to 4, does not apply if editable = false)
42817      */
42818     minChars : 4,
42819     /**
42820      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42821      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42822      */
42823     typeAhead: false,
42824     /**
42825      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42826      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42827      */
42828     queryDelay: 500,
42829     /**
42830      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42831      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42832      */
42833     pageSize: 0,
42834     /**
42835      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42836      * when editable = true (defaults to false)
42837      */
42838     selectOnFocus:false,
42839     /**
42840      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42841      */
42842     queryParam: 'query',
42843     /**
42844      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42845      * when mode = 'remote' (defaults to 'Loading...')
42846      */
42847     loadingText: 'Loading...',
42848     /**
42849      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42850      */
42851     resizable: false,
42852     /**
42853      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42854      */
42855     handleHeight : 8,
42856     /**
42857      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42858      * traditional select (defaults to true)
42859      */
42860     editable: true,
42861     /**
42862      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42863      */
42864     allQuery: '',
42865     /**
42866      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42867      */
42868     mode: 'remote',
42869     /**
42870      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42871      * listWidth has a higher value)
42872      */
42873     minListWidth : 70,
42874     /**
42875      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42876      * allow the user to set arbitrary text into the field (defaults to false)
42877      */
42878     forceSelection:false,
42879     /**
42880      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42881      * if typeAhead = true (defaults to 250)
42882      */
42883     typeAheadDelay : 250,
42884     /**
42885      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42886      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42887      */
42888     valueNotFoundText : undefined,
42889     /**
42890      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42891      */
42892     blockFocus : false,
42893     
42894     /**
42895      * @cfg {Boolean} disableClear Disable showing of clear button.
42896      */
42897     disableClear : false,
42898     /**
42899      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42900      */
42901     alwaysQuery : false,
42902     
42903     //private
42904     addicon : false,
42905     editicon: false,
42906     
42907     // element that contains real text value.. (when hidden is used..)
42908      
42909     // private
42910     onRender : function(ct, position)
42911     {
42912         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42913         
42914         if(this.hiddenName){
42915             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42916                     'before', true);
42917             this.hiddenField.value =
42918                 this.hiddenValue !== undefined ? this.hiddenValue :
42919                 this.value !== undefined ? this.value : '';
42920
42921             // prevent input submission
42922             this.el.dom.removeAttribute('name');
42923              
42924              
42925         }
42926         
42927         if(Roo.isGecko){
42928             this.el.dom.setAttribute('autocomplete', 'off');
42929         }
42930
42931         var cls = 'x-combo-list';
42932
42933         this.list = new Roo.Layer({
42934             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42935         });
42936
42937         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42938         this.list.setWidth(lw);
42939         this.list.swallowEvent('mousewheel');
42940         this.assetHeight = 0;
42941
42942         if(this.title){
42943             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42944             this.assetHeight += this.header.getHeight();
42945         }
42946
42947         this.innerList = this.list.createChild({cls:cls+'-inner'});
42948         this.innerList.on('mouseover', this.onViewOver, this);
42949         this.innerList.on('mousemove', this.onViewMove, this);
42950         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42951         
42952         if(this.allowBlank && !this.pageSize && !this.disableClear){
42953             this.footer = this.list.createChild({cls:cls+'-ft'});
42954             this.pageTb = new Roo.Toolbar(this.footer);
42955            
42956         }
42957         if(this.pageSize){
42958             this.footer = this.list.createChild({cls:cls+'-ft'});
42959             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42960                     {pageSize: this.pageSize});
42961             
42962         }
42963         
42964         if (this.pageTb && this.allowBlank && !this.disableClear) {
42965             var _this = this;
42966             this.pageTb.add(new Roo.Toolbar.Fill(), {
42967                 cls: 'x-btn-icon x-btn-clear',
42968                 text: '&#160;',
42969                 handler: function()
42970                 {
42971                     _this.collapse();
42972                     _this.clearValue();
42973                     _this.onSelect(false, -1);
42974                 }
42975             });
42976         }
42977         if (this.footer) {
42978             this.assetHeight += this.footer.getHeight();
42979         }
42980         
42981
42982         if(!this.tpl){
42983             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42984         }
42985
42986         this.view = new Roo.View(this.innerList, this.tpl, {
42987             singleSelect:true,
42988             store: this.store,
42989             selectedClass: this.selectedClass
42990         });
42991
42992         this.view.on('click', this.onViewClick, this);
42993
42994         this.store.on('beforeload', this.onBeforeLoad, this);
42995         this.store.on('load', this.onLoad, this);
42996         this.store.on('loadexception', this.onLoadException, this);
42997
42998         if(this.resizable){
42999             this.resizer = new Roo.Resizable(this.list,  {
43000                pinned:true, handles:'se'
43001             });
43002             this.resizer.on('resize', function(r, w, h){
43003                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
43004                 this.listWidth = w;
43005                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
43006                 this.restrictHeight();
43007             }, this);
43008             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
43009         }
43010         if(!this.editable){
43011             this.editable = true;
43012             this.setEditable(false);
43013         }  
43014         
43015         
43016         if (typeof(this.events.add.listeners) != 'undefined') {
43017             
43018             this.addicon = this.wrap.createChild(
43019                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
43020        
43021             this.addicon.on('click', function(e) {
43022                 this.fireEvent('add', this);
43023             }, this);
43024         }
43025         if (typeof(this.events.edit.listeners) != 'undefined') {
43026             
43027             this.editicon = this.wrap.createChild(
43028                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
43029             if (this.addicon) {
43030                 this.editicon.setStyle('margin-left', '40px');
43031             }
43032             this.editicon.on('click', function(e) {
43033                 
43034                 // we fire even  if inothing is selected..
43035                 this.fireEvent('edit', this, this.lastData );
43036                 
43037             }, this);
43038         }
43039         
43040         
43041         
43042     },
43043
43044     // private
43045     initEvents : function(){
43046         Roo.form.ComboBox.superclass.initEvents.call(this);
43047
43048         this.keyNav = new Roo.KeyNav(this.el, {
43049             "up" : function(e){
43050                 this.inKeyMode = true;
43051                 this.selectPrev();
43052             },
43053
43054             "down" : function(e){
43055                 if(!this.isExpanded()){
43056                     this.onTriggerClick();
43057                 }else{
43058                     this.inKeyMode = true;
43059                     this.selectNext();
43060                 }
43061             },
43062
43063             "enter" : function(e){
43064                 this.onViewClick();
43065                 //return true;
43066             },
43067
43068             "esc" : function(e){
43069                 this.collapse();
43070             },
43071
43072             "tab" : function(e){
43073                 this.onViewClick(false);
43074                 this.fireEvent("specialkey", this, e);
43075                 return true;
43076             },
43077
43078             scope : this,
43079
43080             doRelay : function(foo, bar, hname){
43081                 if(hname == 'down' || this.scope.isExpanded()){
43082                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43083                 }
43084                 return true;
43085             },
43086
43087             forceKeyDown: true
43088         });
43089         this.queryDelay = Math.max(this.queryDelay || 10,
43090                 this.mode == 'local' ? 10 : 250);
43091         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
43092         if(this.typeAhead){
43093             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
43094         }
43095         if(this.editable !== false){
43096             this.el.on("keyup", this.onKeyUp, this);
43097         }
43098         if(this.forceSelection){
43099             this.on('blur', this.doForce, this);
43100         }
43101     },
43102
43103     onDestroy : function(){
43104         if(this.view){
43105             this.view.setStore(null);
43106             this.view.el.removeAllListeners();
43107             this.view.el.remove();
43108             this.view.purgeListeners();
43109         }
43110         if(this.list){
43111             this.list.destroy();
43112         }
43113         if(this.store){
43114             this.store.un('beforeload', this.onBeforeLoad, this);
43115             this.store.un('load', this.onLoad, this);
43116             this.store.un('loadexception', this.onLoadException, this);
43117         }
43118         Roo.form.ComboBox.superclass.onDestroy.call(this);
43119     },
43120
43121     // private
43122     fireKey : function(e){
43123         if(e.isNavKeyPress() && !this.list.isVisible()){
43124             this.fireEvent("specialkey", this, e);
43125         }
43126     },
43127
43128     // private
43129     onResize: function(w, h){
43130         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
43131         
43132         if(typeof w != 'number'){
43133             // we do not handle it!?!?
43134             return;
43135         }
43136         var tw = this.trigger.getWidth();
43137         tw += this.addicon ? this.addicon.getWidth() : 0;
43138         tw += this.editicon ? this.editicon.getWidth() : 0;
43139         var x = w - tw;
43140         this.el.setWidth( this.adjustWidth('input', x));
43141             
43142         this.trigger.setStyle('left', x+'px');
43143         
43144         if(this.list && this.listWidth === undefined){
43145             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
43146             this.list.setWidth(lw);
43147             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43148         }
43149         
43150     
43151         
43152     },
43153
43154     /**
43155      * Allow or prevent the user from directly editing the field text.  If false is passed,
43156      * the user will only be able to select from the items defined in the dropdown list.  This method
43157      * is the runtime equivalent of setting the 'editable' config option at config time.
43158      * @param {Boolean} value True to allow the user to directly edit the field text
43159      */
43160     setEditable : function(value){
43161         if(value == this.editable){
43162             return;
43163         }
43164         this.editable = value;
43165         if(!value){
43166             this.el.dom.setAttribute('readOnly', true);
43167             this.el.on('mousedown', this.onTriggerClick,  this);
43168             this.el.addClass('x-combo-noedit');
43169         }else{
43170             this.el.dom.setAttribute('readOnly', false);
43171             this.el.un('mousedown', this.onTriggerClick,  this);
43172             this.el.removeClass('x-combo-noedit');
43173         }
43174     },
43175
43176     // private
43177     onBeforeLoad : function(){
43178         if(!this.hasFocus){
43179             return;
43180         }
43181         this.innerList.update(this.loadingText ?
43182                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43183         this.restrictHeight();
43184         this.selectedIndex = -1;
43185     },
43186
43187     // private
43188     onLoad : function(){
43189         if(!this.hasFocus){
43190             return;
43191         }
43192         if(this.store.getCount() > 0){
43193             this.expand();
43194             this.restrictHeight();
43195             if(this.lastQuery == this.allQuery){
43196                 if(this.editable){
43197                     this.el.dom.select();
43198                 }
43199                 if(!this.selectByValue(this.value, true)){
43200                     this.select(0, true);
43201                 }
43202             }else{
43203                 this.selectNext();
43204                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
43205                     this.taTask.delay(this.typeAheadDelay);
43206                 }
43207             }
43208         }else{
43209             this.onEmptyResults();
43210         }
43211         //this.el.focus();
43212     },
43213     // private
43214     onLoadException : function()
43215     {
43216         this.collapse();
43217         Roo.log(this.store.reader.jsonData);
43218         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43219             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43220         }
43221         
43222         
43223     },
43224     // private
43225     onTypeAhead : function(){
43226         if(this.store.getCount() > 0){
43227             var r = this.store.getAt(0);
43228             var newValue = r.data[this.displayField];
43229             var len = newValue.length;
43230             var selStart = this.getRawValue().length;
43231             if(selStart != len){
43232                 this.setRawValue(newValue);
43233                 this.selectText(selStart, newValue.length);
43234             }
43235         }
43236     },
43237
43238     // private
43239     onSelect : function(record, index){
43240         if(this.fireEvent('beforeselect', this, record, index) !== false){
43241             this.setFromData(index > -1 ? record.data : false);
43242             this.collapse();
43243             this.fireEvent('select', this, record, index);
43244         }
43245     },
43246
43247     /**
43248      * Returns the currently selected field value or empty string if no value is set.
43249      * @return {String} value The selected value
43250      */
43251     getValue : function(){
43252         if(this.valueField){
43253             return typeof this.value != 'undefined' ? this.value : '';
43254         }
43255         return Roo.form.ComboBox.superclass.getValue.call(this);
43256     },
43257
43258     /**
43259      * Clears any text/value currently set in the field
43260      */
43261     clearValue : function(){
43262         if(this.hiddenField){
43263             this.hiddenField.value = '';
43264         }
43265         this.value = '';
43266         this.setRawValue('');
43267         this.lastSelectionText = '';
43268         
43269     },
43270
43271     /**
43272      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
43273      * will be displayed in the field.  If the value does not match the data value of an existing item,
43274      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
43275      * Otherwise the field will be blank (although the value will still be set).
43276      * @param {String} value The value to match
43277      */
43278     setValue : function(v){
43279         var text = v;
43280         if(this.valueField){
43281             var r = this.findRecord(this.valueField, v);
43282             if(r){
43283                 text = r.data[this.displayField];
43284             }else if(this.valueNotFoundText !== undefined){
43285                 text = this.valueNotFoundText;
43286             }
43287         }
43288         this.lastSelectionText = text;
43289         if(this.hiddenField){
43290             this.hiddenField.value = v;
43291         }
43292         Roo.form.ComboBox.superclass.setValue.call(this, text);
43293         this.value = v;
43294     },
43295     /**
43296      * @property {Object} the last set data for the element
43297      */
43298     
43299     lastData : false,
43300     /**
43301      * Sets the value of the field based on a object which is related to the record format for the store.
43302      * @param {Object} value the value to set as. or false on reset?
43303      */
43304     setFromData : function(o){
43305         var dv = ''; // display value
43306         var vv = ''; // value value..
43307         this.lastData = o;
43308         if (this.displayField) {
43309             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
43310         } else {
43311             // this is an error condition!!!
43312             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
43313         }
43314         
43315         if(this.valueField){
43316             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
43317         }
43318         if(this.hiddenField){
43319             this.hiddenField.value = vv;
43320             
43321             this.lastSelectionText = dv;
43322             Roo.form.ComboBox.superclass.setValue.call(this, dv);
43323             this.value = vv;
43324             return;
43325         }
43326         // no hidden field.. - we store the value in 'value', but still display
43327         // display field!!!!
43328         this.lastSelectionText = dv;
43329         Roo.form.ComboBox.superclass.setValue.call(this, dv);
43330         this.value = vv;
43331         
43332         
43333     },
43334     // private
43335     reset : function(){
43336         // overridden so that last data is reset..
43337         this.setValue(this.resetValue);
43338         this.originalValue = this.getValue();
43339         this.clearInvalid();
43340         this.lastData = false;
43341         if (this.view) {
43342             this.view.clearSelections();
43343         }
43344     },
43345     // private
43346     findRecord : function(prop, value){
43347         var record;
43348         if(this.store.getCount() > 0){
43349             this.store.each(function(r){
43350                 if(r.data[prop] == value){
43351                     record = r;
43352                     return false;
43353                 }
43354                 return true;
43355             });
43356         }
43357         return record;
43358     },
43359     
43360     getName: function()
43361     {
43362         // returns hidden if it's set..
43363         if (!this.rendered) {return ''};
43364         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
43365         
43366     },
43367     // private
43368     onViewMove : function(e, t){
43369         this.inKeyMode = false;
43370     },
43371
43372     // private
43373     onViewOver : function(e, t){
43374         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43375             return;
43376         }
43377         var item = this.view.findItemFromChild(t);
43378         if(item){
43379             var index = this.view.indexOf(item);
43380             this.select(index, false);
43381         }
43382     },
43383
43384     // private
43385     onViewClick : function(doFocus)
43386     {
43387         var index = this.view.getSelectedIndexes()[0];
43388         var r = this.store.getAt(index);
43389         if(r){
43390             this.onSelect(r, index);
43391         }
43392         if(doFocus !== false && !this.blockFocus){
43393             this.el.focus();
43394         }
43395     },
43396
43397     // private
43398     restrictHeight : function(){
43399         this.innerList.dom.style.height = '';
43400         var inner = this.innerList.dom;
43401         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43402         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43403         this.list.beginUpdate();
43404         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43405         this.list.alignTo(this.el, this.listAlign);
43406         this.list.endUpdate();
43407     },
43408
43409     // private
43410     onEmptyResults : function(){
43411         this.collapse();
43412     },
43413
43414     /**
43415      * Returns true if the dropdown list is expanded, else false.
43416      */
43417     isExpanded : function(){
43418         return this.list.isVisible();
43419     },
43420
43421     /**
43422      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43423      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43424      * @param {String} value The data value of the item to select
43425      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43426      * selected item if it is not currently in view (defaults to true)
43427      * @return {Boolean} True if the value matched an item in the list, else false
43428      */
43429     selectByValue : function(v, scrollIntoView){
43430         if(v !== undefined && v !== null){
43431             var r = this.findRecord(this.valueField || this.displayField, v);
43432             if(r){
43433                 this.select(this.store.indexOf(r), scrollIntoView);
43434                 return true;
43435             }
43436         }
43437         return false;
43438     },
43439
43440     /**
43441      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43442      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43443      * @param {Number} index The zero-based index of the list item to select
43444      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43445      * selected item if it is not currently in view (defaults to true)
43446      */
43447     select : function(index, scrollIntoView){
43448         this.selectedIndex = index;
43449         this.view.select(index);
43450         if(scrollIntoView !== false){
43451             var el = this.view.getNode(index);
43452             if(el){
43453                 this.innerList.scrollChildIntoView(el, false);
43454             }
43455         }
43456     },
43457
43458     // private
43459     selectNext : function(){
43460         var ct = this.store.getCount();
43461         if(ct > 0){
43462             if(this.selectedIndex == -1){
43463                 this.select(0);
43464             }else if(this.selectedIndex < ct-1){
43465                 this.select(this.selectedIndex+1);
43466             }
43467         }
43468     },
43469
43470     // private
43471     selectPrev : function(){
43472         var ct = this.store.getCount();
43473         if(ct > 0){
43474             if(this.selectedIndex == -1){
43475                 this.select(0);
43476             }else if(this.selectedIndex != 0){
43477                 this.select(this.selectedIndex-1);
43478             }
43479         }
43480     },
43481
43482     // private
43483     onKeyUp : function(e){
43484         if(this.editable !== false && !e.isSpecialKey()){
43485             this.lastKey = e.getKey();
43486             this.dqTask.delay(this.queryDelay);
43487         }
43488     },
43489
43490     // private
43491     validateBlur : function(){
43492         return !this.list || !this.list.isVisible();   
43493     },
43494
43495     // private
43496     initQuery : function(){
43497         this.doQuery(this.getRawValue());
43498     },
43499
43500     // private
43501     doForce : function(){
43502         if(this.el.dom.value.length > 0){
43503             this.el.dom.value =
43504                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43505              
43506         }
43507     },
43508
43509     /**
43510      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43511      * query allowing the query action to be canceled if needed.
43512      * @param {String} query The SQL query to execute
43513      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43514      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43515      * saved in the current store (defaults to false)
43516      */
43517     doQuery : function(q, forceAll){
43518         if(q === undefined || q === null){
43519             q = '';
43520         }
43521         var qe = {
43522             query: q,
43523             forceAll: forceAll,
43524             combo: this,
43525             cancel:false
43526         };
43527         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43528             return false;
43529         }
43530         q = qe.query;
43531         forceAll = qe.forceAll;
43532         if(forceAll === true || (q.length >= this.minChars)){
43533             if(this.lastQuery != q || this.alwaysQuery){
43534                 this.lastQuery = q;
43535                 if(this.mode == 'local'){
43536                     this.selectedIndex = -1;
43537                     if(forceAll){
43538                         this.store.clearFilter();
43539                     }else{
43540                         this.store.filter(this.displayField, q);
43541                     }
43542                     this.onLoad();
43543                 }else{
43544                     this.store.baseParams[this.queryParam] = q;
43545                     this.store.load({
43546                         params: this.getParams(q)
43547                     });
43548                     this.expand();
43549                 }
43550             }else{
43551                 this.selectedIndex = -1;
43552                 this.onLoad();   
43553             }
43554         }
43555     },
43556
43557     // private
43558     getParams : function(q){
43559         var p = {};
43560         //p[this.queryParam] = q;
43561         if(this.pageSize){
43562             p.start = 0;
43563             p.limit = this.pageSize;
43564         }
43565         return p;
43566     },
43567
43568     /**
43569      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43570      */
43571     collapse : function(){
43572         if(!this.isExpanded()){
43573             return;
43574         }
43575         this.list.hide();
43576         Roo.get(document).un('mousedown', this.collapseIf, this);
43577         Roo.get(document).un('mousewheel', this.collapseIf, this);
43578         if (!this.editable) {
43579             Roo.get(document).un('keydown', this.listKeyPress, this);
43580         }
43581         this.fireEvent('collapse', this);
43582     },
43583
43584     // private
43585     collapseIf : function(e){
43586         if(!e.within(this.wrap) && !e.within(this.list)){
43587             this.collapse();
43588         }
43589     },
43590
43591     /**
43592      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43593      */
43594     expand : function(){
43595         if(this.isExpanded() || !this.hasFocus){
43596             return;
43597         }
43598         this.list.alignTo(this.el, this.listAlign);
43599         this.list.show();
43600         Roo.get(document).on('mousedown', this.collapseIf, this);
43601         Roo.get(document).on('mousewheel', this.collapseIf, this);
43602         if (!this.editable) {
43603             Roo.get(document).on('keydown', this.listKeyPress, this);
43604         }
43605         
43606         this.fireEvent('expand', this);
43607     },
43608
43609     // private
43610     // Implements the default empty TriggerField.onTriggerClick function
43611     onTriggerClick : function(){
43612         if(this.disabled){
43613             return;
43614         }
43615         if(this.isExpanded()){
43616             this.collapse();
43617             if (!this.blockFocus) {
43618                 this.el.focus();
43619             }
43620             
43621         }else {
43622             this.hasFocus = true;
43623             if(this.triggerAction == 'all') {
43624                 this.doQuery(this.allQuery, true);
43625             } else {
43626                 this.doQuery(this.getRawValue());
43627             }
43628             if (!this.blockFocus) {
43629                 this.el.focus();
43630             }
43631         }
43632     },
43633     listKeyPress : function(e)
43634     {
43635         //Roo.log('listkeypress');
43636         // scroll to first matching element based on key pres..
43637         if (e.isSpecialKey()) {
43638             return false;
43639         }
43640         var k = String.fromCharCode(e.getKey()).toUpperCase();
43641         //Roo.log(k);
43642         var match  = false;
43643         var csel = this.view.getSelectedNodes();
43644         var cselitem = false;
43645         if (csel.length) {
43646             var ix = this.view.indexOf(csel[0]);
43647             cselitem  = this.store.getAt(ix);
43648             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43649                 cselitem = false;
43650             }
43651             
43652         }
43653         
43654         this.store.each(function(v) { 
43655             if (cselitem) {
43656                 // start at existing selection.
43657                 if (cselitem.id == v.id) {
43658                     cselitem = false;
43659                 }
43660                 return;
43661             }
43662                 
43663             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43664                 match = this.store.indexOf(v);
43665                 return false;
43666             }
43667         }, this);
43668         
43669         if (match === false) {
43670             return true; // no more action?
43671         }
43672         // scroll to?
43673         this.view.select(match);
43674         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43675         sn.scrollIntoView(sn.dom.parentNode, false);
43676     } 
43677
43678     /** 
43679     * @cfg {Boolean} grow 
43680     * @hide 
43681     */
43682     /** 
43683     * @cfg {Number} growMin 
43684     * @hide 
43685     */
43686     /** 
43687     * @cfg {Number} growMax 
43688     * @hide 
43689     */
43690     /**
43691      * @hide
43692      * @method autoSize
43693      */
43694 });/*
43695  * Copyright(c) 2010-2012, Roo J Solutions Limited
43696  *
43697  * Licence LGPL
43698  *
43699  */
43700
43701 /**
43702  * @class Roo.form.ComboBoxArray
43703  * @extends Roo.form.TextField
43704  * A facebook style adder... for lists of email / people / countries  etc...
43705  * pick multiple items from a combo box, and shows each one.
43706  *
43707  *  Fred [x]  Brian [x]  [Pick another |v]
43708  *
43709  *
43710  *  For this to work: it needs various extra information
43711  *    - normal combo problay has
43712  *      name, hiddenName
43713  *    + displayField, valueField
43714  *
43715  *    For our purpose...
43716  *
43717  *
43718  *   If we change from 'extends' to wrapping...
43719  *   
43720  *  
43721  *
43722  
43723  
43724  * @constructor
43725  * Create a new ComboBoxArray.
43726  * @param {Object} config Configuration options
43727  */
43728  
43729
43730 Roo.form.ComboBoxArray = function(config)
43731 {
43732     this.addEvents({
43733         /**
43734          * @event beforeremove
43735          * Fires before remove the value from the list
43736              * @param {Roo.form.ComboBoxArray} _self This combo box array
43737              * @param {Roo.form.ComboBoxArray.Item} item removed item
43738              */
43739         'beforeremove' : true,
43740         /**
43741          * @event remove
43742          * Fires when remove the value from the list
43743              * @param {Roo.form.ComboBoxArray} _self This combo box array
43744              * @param {Roo.form.ComboBoxArray.Item} item removed item
43745              */
43746         'remove' : true
43747         
43748         
43749     });
43750     
43751     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43752     
43753     this.items = new Roo.util.MixedCollection(false);
43754     
43755     // construct the child combo...
43756     
43757     
43758     
43759     
43760    
43761     
43762 }
43763
43764  
43765 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43766
43767     /**
43768      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43769      */
43770     
43771     lastData : false,
43772     
43773     // behavies liek a hiddne field
43774     inputType:      'hidden',
43775     /**
43776      * @cfg {Number} width The width of the box that displays the selected element
43777      */ 
43778     width:          300,
43779
43780     
43781     
43782     /**
43783      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43784      */
43785     name : false,
43786     /**
43787      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43788      */
43789     hiddenName : false,
43790       /**
43791      * @cfg {String} seperator    The value seperator normally ',' 
43792      */
43793     seperator : ',',
43794     
43795     // private the array of items that are displayed..
43796     items  : false,
43797     // private - the hidden field el.
43798     hiddenEl : false,
43799     // private - the filed el..
43800     el : false,
43801     
43802     //validateValue : function() { return true; }, // all values are ok!
43803     //onAddClick: function() { },
43804     
43805     onRender : function(ct, position) 
43806     {
43807         
43808         // create the standard hidden element
43809         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43810         
43811         
43812         // give fake names to child combo;
43813         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43814         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43815         
43816         this.combo = Roo.factory(this.combo, Roo.form);
43817         this.combo.onRender(ct, position);
43818         if (typeof(this.combo.width) != 'undefined') {
43819             this.combo.onResize(this.combo.width,0);
43820         }
43821         
43822         this.combo.initEvents();
43823         
43824         // assigned so form know we need to do this..
43825         this.store          = this.combo.store;
43826         this.valueField     = this.combo.valueField;
43827         this.displayField   = this.combo.displayField ;
43828         
43829         
43830         this.combo.wrap.addClass('x-cbarray-grp');
43831         
43832         var cbwrap = this.combo.wrap.createChild(
43833             {tag: 'div', cls: 'x-cbarray-cb'},
43834             this.combo.el.dom
43835         );
43836         
43837              
43838         this.hiddenEl = this.combo.wrap.createChild({
43839             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43840         });
43841         this.el = this.combo.wrap.createChild({
43842             tag: 'input',  type:'hidden' , name: this.name, value : ''
43843         });
43844          //   this.el.dom.removeAttribute("name");
43845         
43846         
43847         this.outerWrap = this.combo.wrap;
43848         this.wrap = cbwrap;
43849         
43850         this.outerWrap.setWidth(this.width);
43851         this.outerWrap.dom.removeChild(this.el.dom);
43852         
43853         this.wrap.dom.appendChild(this.el.dom);
43854         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43855         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43856         
43857         this.combo.trigger.setStyle('position','relative');
43858         this.combo.trigger.setStyle('left', '0px');
43859         this.combo.trigger.setStyle('top', '2px');
43860         
43861         this.combo.el.setStyle('vertical-align', 'text-bottom');
43862         
43863         //this.trigger.setStyle('vertical-align', 'top');
43864         
43865         // this should use the code from combo really... on('add' ....)
43866         if (this.adder) {
43867             
43868         
43869             this.adder = this.outerWrap.createChild(
43870                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43871             var _t = this;
43872             this.adder.on('click', function(e) {
43873                 _t.fireEvent('adderclick', this, e);
43874             }, _t);
43875         }
43876         //var _t = this;
43877         //this.adder.on('click', this.onAddClick, _t);
43878         
43879         
43880         this.combo.on('select', function(cb, rec, ix) {
43881             this.addItem(rec.data);
43882             
43883             cb.setValue('');
43884             cb.el.dom.value = '';
43885             //cb.lastData = rec.data;
43886             // add to list
43887             
43888         }, this);
43889         
43890         
43891     },
43892     
43893     
43894     getName: function()
43895     {
43896         // returns hidden if it's set..
43897         if (!this.rendered) {return ''};
43898         return  this.hiddenName ? this.hiddenName : this.name;
43899         
43900     },
43901     
43902     
43903     onResize: function(w, h){
43904         
43905         return;
43906         // not sure if this is needed..
43907         //this.combo.onResize(w,h);
43908         
43909         if(typeof w != 'number'){
43910             // we do not handle it!?!?
43911             return;
43912         }
43913         var tw = this.combo.trigger.getWidth();
43914         tw += this.addicon ? this.addicon.getWidth() : 0;
43915         tw += this.editicon ? this.editicon.getWidth() : 0;
43916         var x = w - tw;
43917         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43918             
43919         this.combo.trigger.setStyle('left', '0px');
43920         
43921         if(this.list && this.listWidth === undefined){
43922             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43923             this.list.setWidth(lw);
43924             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43925         }
43926         
43927     
43928         
43929     },
43930     
43931     addItem: function(rec)
43932     {
43933         var valueField = this.combo.valueField;
43934         var displayField = this.combo.displayField;
43935         
43936         if (this.items.indexOfKey(rec[valueField]) > -1) {
43937             //console.log("GOT " + rec.data.id);
43938             return;
43939         }
43940         
43941         var x = new Roo.form.ComboBoxArray.Item({
43942             //id : rec[this.idField],
43943             data : rec,
43944             displayField : displayField ,
43945             tipField : displayField ,
43946             cb : this
43947         });
43948         // use the 
43949         this.items.add(rec[valueField],x);
43950         // add it before the element..
43951         this.updateHiddenEl();
43952         x.render(this.outerWrap, this.wrap.dom);
43953         // add the image handler..
43954     },
43955     
43956     updateHiddenEl : function()
43957     {
43958         this.validate();
43959         if (!this.hiddenEl) {
43960             return;
43961         }
43962         var ar = [];
43963         var idField = this.combo.valueField;
43964         
43965         this.items.each(function(f) {
43966             ar.push(f.data[idField]);
43967         });
43968         this.hiddenEl.dom.value = ar.join(this.seperator);
43969         this.validate();
43970     },
43971     
43972     reset : function()
43973     {
43974         this.items.clear();
43975         
43976         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43977            el.remove();
43978         });
43979         
43980         this.el.dom.value = '';
43981         if (this.hiddenEl) {
43982             this.hiddenEl.dom.value = '';
43983         }
43984         
43985     },
43986     getValue: function()
43987     {
43988         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43989     },
43990     setValue: function(v) // not a valid action - must use addItems..
43991     {
43992         
43993         this.reset();
43994          
43995         if (this.store.isLocal && (typeof(v) == 'string')) {
43996             // then we can use the store to find the values..
43997             // comma seperated at present.. this needs to allow JSON based encoding..
43998             this.hiddenEl.value  = v;
43999             var v_ar = [];
44000             Roo.each(v.split(this.seperator), function(k) {
44001                 Roo.log("CHECK " + this.valueField + ',' + k);
44002                 var li = this.store.query(this.valueField, k);
44003                 if (!li.length) {
44004                     return;
44005                 }
44006                 var add = {};
44007                 add[this.valueField] = k;
44008                 add[this.displayField] = li.item(0).data[this.displayField];
44009                 
44010                 this.addItem(add);
44011             }, this) 
44012              
44013         }
44014         if (typeof(v) == 'object' ) {
44015             // then let's assume it's an array of objects..
44016             Roo.each(v, function(l) {
44017                 var add = l;
44018                 if (typeof(l) == 'string') {
44019                     add = {};
44020                     add[this.valueField] = l;
44021                     add[this.displayField] = l
44022                 }
44023                 this.addItem(add);
44024             }, this);
44025              
44026         }
44027         
44028         
44029     },
44030     setFromData: function(v)
44031     {
44032         // this recieves an object, if setValues is called.
44033         this.reset();
44034         this.el.dom.value = v[this.displayField];
44035         this.hiddenEl.dom.value = v[this.valueField];
44036         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
44037             return;
44038         }
44039         var kv = v[this.valueField];
44040         var dv = v[this.displayField];
44041         kv = typeof(kv) != 'string' ? '' : kv;
44042         dv = typeof(dv) != 'string' ? '' : dv;
44043         
44044         
44045         var keys = kv.split(this.seperator);
44046         var display = dv.split(this.seperator);
44047         for (var i = 0 ; i < keys.length; i++) {
44048             add = {};
44049             add[this.valueField] = keys[i];
44050             add[this.displayField] = display[i];
44051             this.addItem(add);
44052         }
44053       
44054         
44055     },
44056     
44057     /**
44058      * Validates the combox array value
44059      * @return {Boolean} True if the value is valid, else false
44060      */
44061     validate : function(){
44062         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
44063             this.clearInvalid();
44064             return true;
44065         }
44066         return false;
44067     },
44068     
44069     validateValue : function(value){
44070         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
44071         
44072     },
44073     
44074     /*@
44075      * overide
44076      * 
44077      */
44078     isDirty : function() {
44079         if(this.disabled) {
44080             return false;
44081         }
44082         
44083         try {
44084             var d = Roo.decode(String(this.originalValue));
44085         } catch (e) {
44086             return String(this.getValue()) !== String(this.originalValue);
44087         }
44088         
44089         var originalValue = [];
44090         
44091         for (var i = 0; i < d.length; i++){
44092             originalValue.push(d[i][this.valueField]);
44093         }
44094         
44095         return String(this.getValue()) !== String(originalValue.join(this.seperator));
44096         
44097     }
44098     
44099 });
44100
44101
44102
44103 /**
44104  * @class Roo.form.ComboBoxArray.Item
44105  * @extends Roo.BoxComponent
44106  * A selected item in the list
44107  *  Fred [x]  Brian [x]  [Pick another |v]
44108  * 
44109  * @constructor
44110  * Create a new item.
44111  * @param {Object} config Configuration options
44112  */
44113  
44114 Roo.form.ComboBoxArray.Item = function(config) {
44115     config.id = Roo.id();
44116     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
44117 }
44118
44119 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
44120     data : {},
44121     cb: false,
44122     displayField : false,
44123     tipField : false,
44124     
44125     
44126     defaultAutoCreate : {
44127         tag: 'div',
44128         cls: 'x-cbarray-item',
44129         cn : [ 
44130             { tag: 'div' },
44131             {
44132                 tag: 'img',
44133                 width:16,
44134                 height : 16,
44135                 src : Roo.BLANK_IMAGE_URL ,
44136                 align: 'center'
44137             }
44138         ]
44139         
44140     },
44141     
44142  
44143     onRender : function(ct, position)
44144     {
44145         Roo.form.Field.superclass.onRender.call(this, ct, position);
44146         
44147         if(!this.el){
44148             var cfg = this.getAutoCreate();
44149             this.el = ct.createChild(cfg, position);
44150         }
44151         
44152         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
44153         
44154         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
44155             this.cb.renderer(this.data) :
44156             String.format('{0}',this.data[this.displayField]);
44157         
44158             
44159         this.el.child('div').dom.setAttribute('qtip',
44160                         String.format('{0}',this.data[this.tipField])
44161         );
44162         
44163         this.el.child('img').on('click', this.remove, this);
44164         
44165     },
44166    
44167     remove : function()
44168     {
44169         if(this.cb.disabled){
44170             return;
44171         }
44172         
44173         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
44174             this.cb.items.remove(this);
44175             this.el.child('img').un('click', this.remove, this);
44176             this.el.remove();
44177             this.cb.updateHiddenEl();
44178
44179             this.cb.fireEvent('remove', this.cb, this);
44180         }
44181         
44182     }
44183 });/*
44184  * RooJS Library 1.1.1
44185  * Copyright(c) 2008-2011  Alan Knowles
44186  *
44187  * License - LGPL
44188  */
44189  
44190
44191 /**
44192  * @class Roo.form.ComboNested
44193  * @extends Roo.form.ComboBox
44194  * A combobox for that allows selection of nested items in a list,
44195  * eg.
44196  *
44197  *  Book
44198  *    -> red
44199  *    -> green
44200  *  Table
44201  *    -> square
44202  *      ->red
44203  *      ->green
44204  *    -> rectangle
44205  *      ->green
44206  *      
44207  * 
44208  * @constructor
44209  * Create a new ComboNested
44210  * @param {Object} config Configuration options
44211  */
44212 Roo.form.ComboNested = function(config){
44213     Roo.form.ComboCheck.superclass.constructor.call(this, config);
44214     // should verify some data...
44215     // like
44216     // hiddenName = required..
44217     // displayField = required
44218     // valudField == required
44219     var req= [ 'hiddenName', 'displayField', 'valueField' ];
44220     var _t = this;
44221     Roo.each(req, function(e) {
44222         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
44223             throw "Roo.form.ComboNested : missing value for: " + e;
44224         }
44225     });
44226      
44227     
44228 };
44229
44230 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
44231    
44232     /*
44233      * @config {Number} max Number of columns to show
44234      */
44235     
44236     maxColumns : 3,
44237    
44238     list : null, // the outermost div..
44239     innerLists : null, // the
44240     views : null,
44241     stores : null,
44242     // private
44243     loadingChildren : false,
44244     
44245     onRender : function(ct, position)
44246     {
44247         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
44248         
44249         if(this.hiddenName){
44250             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
44251                     'before', true);
44252             this.hiddenField.value =
44253                 this.hiddenValue !== undefined ? this.hiddenValue :
44254                 this.value !== undefined ? this.value : '';
44255
44256             // prevent input submission
44257             this.el.dom.removeAttribute('name');
44258              
44259              
44260         }
44261         
44262         if(Roo.isGecko){
44263             this.el.dom.setAttribute('autocomplete', 'off');
44264         }
44265
44266         var cls = 'x-combo-list';
44267
44268         this.list = new Roo.Layer({
44269             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
44270         });
44271
44272         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
44273         this.list.setWidth(lw);
44274         this.list.swallowEvent('mousewheel');
44275         this.assetHeight = 0;
44276
44277         if(this.title){
44278             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
44279             this.assetHeight += this.header.getHeight();
44280         }
44281         this.innerLists = [];
44282         this.views = [];
44283         this.stores = [];
44284         for (var i =0 ; i < this.maxColumns; i++) {
44285             this.onRenderList( cls, i);
44286         }
44287         
44288         // always needs footer, as we are going to have an 'OK' button.
44289         this.footer = this.list.createChild({cls:cls+'-ft'});
44290         this.pageTb = new Roo.Toolbar(this.footer);  
44291         var _this = this;
44292         this.pageTb.add(  {
44293             
44294             text: 'Done',
44295             handler: function()
44296             {
44297                 _this.collapse();
44298             }
44299         });
44300         
44301         if ( this.allowBlank && !this.disableClear) {
44302             
44303             this.pageTb.add(new Roo.Toolbar.Fill(), {
44304                 cls: 'x-btn-icon x-btn-clear',
44305                 text: '&#160;',
44306                 handler: function()
44307                 {
44308                     _this.collapse();
44309                     _this.clearValue();
44310                     _this.onSelect(false, -1);
44311                 }
44312             });
44313         }
44314         if (this.footer) {
44315             this.assetHeight += this.footer.getHeight();
44316         }
44317         
44318     },
44319     onRenderList : function (  cls, i)
44320     {
44321         
44322         var lw = Math.floor(
44323                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44324         );
44325         
44326         this.list.setWidth(lw); // default to '1'
44327
44328         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
44329         //il.on('mouseover', this.onViewOver, this, { list:  i });
44330         //il.on('mousemove', this.onViewMove, this, { list:  i });
44331         il.setWidth(lw);
44332         il.setStyle({ 'overflow-x' : 'hidden'});
44333
44334         if(!this.tpl){
44335             this.tpl = new Roo.Template({
44336                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44337                 isEmpty: function (value, allValues) {
44338                     //Roo.log(value);
44339                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44340                     return dl ? 'has-children' : 'no-children'
44341                 }
44342             });
44343         }
44344         
44345         var store  = this.store;
44346         if (i > 0) {
44347             store  = new Roo.data.SimpleStore({
44348                 //fields : this.store.reader.meta.fields,
44349                 reader : this.store.reader,
44350                 data : [ ]
44351             });
44352         }
44353         this.stores[i]  = store;
44354                   
44355         var view = this.views[i] = new Roo.View(
44356             il,
44357             this.tpl,
44358             {
44359                 singleSelect:true,
44360                 store: store,
44361                 selectedClass: this.selectedClass
44362             }
44363         );
44364         view.getEl().setWidth(lw);
44365         view.getEl().setStyle({
44366             position: i < 1 ? 'relative' : 'absolute',
44367             top: 0,
44368             left: (i * lw ) + 'px',
44369             display : i > 0 ? 'none' : 'block'
44370         });
44371         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44372         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44373         //view.on('click', this.onViewClick, this, { list : i });
44374
44375         store.on('beforeload', this.onBeforeLoad, this);
44376         store.on('load',  this.onLoad, this, { list  : i});
44377         store.on('loadexception', this.onLoadException, this);
44378
44379         // hide the other vies..
44380         
44381         
44382         
44383     },
44384       
44385     restrictHeight : function()
44386     {
44387         var mh = 0;
44388         Roo.each(this.innerLists, function(il,i) {
44389             var el = this.views[i].getEl();
44390             el.dom.style.height = '';
44391             var inner = el.dom;
44392             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44393             // only adjust heights on other ones..
44394             mh = Math.max(h, mh);
44395             if (i < 1) {
44396                 
44397                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44398                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44399                
44400             }
44401             
44402             
44403         }, this);
44404         
44405         this.list.beginUpdate();
44406         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44407         this.list.alignTo(this.el, this.listAlign);
44408         this.list.endUpdate();
44409         
44410     },
44411      
44412     
44413     // -- store handlers..
44414     // private
44415     onBeforeLoad : function()
44416     {
44417         if(!this.hasFocus){
44418             return;
44419         }
44420         this.innerLists[0].update(this.loadingText ?
44421                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44422         this.restrictHeight();
44423         this.selectedIndex = -1;
44424     },
44425     // private
44426     onLoad : function(a,b,c,d)
44427     {
44428         if (!this.loadingChildren) {
44429             // then we are loading the top level. - hide the children
44430             for (var i = 1;i < this.views.length; i++) {
44431                 this.views[i].getEl().setStyle({ display : 'none' });
44432             }
44433             var lw = Math.floor(
44434                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44435             );
44436         
44437              this.list.setWidth(lw); // default to '1'
44438
44439             
44440         }
44441         if(!this.hasFocus){
44442             return;
44443         }
44444         
44445         if(this.store.getCount() > 0) {
44446             this.expand();
44447             this.restrictHeight();   
44448         } else {
44449             this.onEmptyResults();
44450         }
44451         
44452         if (!this.loadingChildren) {
44453             this.selectActive();
44454         }
44455         /*
44456         this.stores[1].loadData([]);
44457         this.stores[2].loadData([]);
44458         this.views
44459         */    
44460     
44461         //this.el.focus();
44462     },
44463     
44464     
44465     // private
44466     onLoadException : function()
44467     {
44468         this.collapse();
44469         Roo.log(this.store.reader.jsonData);
44470         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44471             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44472         }
44473         
44474         
44475     },
44476     // no cleaning of leading spaces on blur here.
44477     cleanLeadingSpace : function(e) { },
44478     
44479
44480     onSelectChange : function (view, sels, opts )
44481     {
44482         var ix = view.getSelectedIndexes();
44483          
44484         if (opts.list > this.maxColumns - 2) {
44485             if (view.store.getCount()<  1) {
44486                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44487
44488             } else  {
44489                 if (ix.length) {
44490                     // used to clear ?? but if we are loading unselected 
44491                     this.setFromData(view.store.getAt(ix[0]).data);
44492                 }
44493                 
44494             }
44495             
44496             return;
44497         }
44498         
44499         if (!ix.length) {
44500             // this get's fired when trigger opens..
44501            // this.setFromData({});
44502             var str = this.stores[opts.list+1];
44503             str.data.clear(); // removeall wihtout the fire events..
44504             return;
44505         }
44506         
44507         var rec = view.store.getAt(ix[0]);
44508          
44509         this.setFromData(rec.data);
44510         this.fireEvent('select', this, rec, ix[0]);
44511         
44512         var lw = Math.floor(
44513              (
44514                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44515              ) / this.maxColumns
44516         );
44517         this.loadingChildren = true;
44518         this.stores[opts.list+1].loadDataFromChildren( rec );
44519         this.loadingChildren = false;
44520         var dl = this.stores[opts.list+1]. getTotalCount();
44521         
44522         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44523         
44524         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44525         for (var i = opts.list+2; i < this.views.length;i++) {
44526             this.views[i].getEl().setStyle({ display : 'none' });
44527         }
44528         
44529         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44530         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44531         
44532         if (this.isLoading) {
44533            // this.selectActive(opts.list);
44534         }
44535          
44536     },
44537     
44538     
44539     
44540     
44541     onDoubleClick : function()
44542     {
44543         this.collapse(); //??
44544     },
44545     
44546      
44547     
44548     
44549     
44550     // private
44551     recordToStack : function(store, prop, value, stack)
44552     {
44553         var cstore = new Roo.data.SimpleStore({
44554             //fields : this.store.reader.meta.fields, // we need array reader.. for
44555             reader : this.store.reader,
44556             data : [ ]
44557         });
44558         var _this = this;
44559         var record  = false;
44560         var srec = false;
44561         if(store.getCount() < 1){
44562             return false;
44563         }
44564         store.each(function(r){
44565             if(r.data[prop] == value){
44566                 record = r;
44567             srec = r;
44568                 return false;
44569             }
44570             if (r.data.cn && r.data.cn.length) {
44571                 cstore.loadDataFromChildren( r);
44572                 var cret = _this.recordToStack(cstore, prop, value, stack);
44573                 if (cret !== false) {
44574                     record = cret;
44575                     srec = r;
44576                     return false;
44577                 }
44578             }
44579              
44580             return true;
44581         });
44582         if (record == false) {
44583             return false
44584         }
44585         stack.unshift(srec);
44586         return record;
44587     },
44588     
44589     /*
44590      * find the stack of stores that match our value.
44591      *
44592      * 
44593      */
44594     
44595     selectActive : function ()
44596     {
44597         // if store is not loaded, then we will need to wait for that to happen first.
44598         var stack = [];
44599         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44600         for (var i = 0; i < stack.length; i++ ) {
44601             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44602         }
44603         
44604     }
44605         
44606          
44607     
44608     
44609     
44610     
44611 });/*
44612  * Based on:
44613  * Ext JS Library 1.1.1
44614  * Copyright(c) 2006-2007, Ext JS, LLC.
44615  *
44616  * Originally Released Under LGPL - original licence link has changed is not relivant.
44617  *
44618  * Fork - LGPL
44619  * <script type="text/javascript">
44620  */
44621 /**
44622  * @class Roo.form.Checkbox
44623  * @extends Roo.form.Field
44624  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44625  * @constructor
44626  * Creates a new Checkbox
44627  * @param {Object} config Configuration options
44628  */
44629 Roo.form.Checkbox = function(config){
44630     Roo.form.Checkbox.superclass.constructor.call(this, config);
44631     this.addEvents({
44632         /**
44633          * @event check
44634          * Fires when the checkbox is checked or unchecked.
44635              * @param {Roo.form.Checkbox} this This checkbox
44636              * @param {Boolean} checked The new checked value
44637              */
44638         check : true
44639     });
44640 };
44641
44642 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44643     /**
44644      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44645      */
44646     focusClass : undefined,
44647     /**
44648      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44649      */
44650     fieldClass: "x-form-field",
44651     /**
44652      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44653      */
44654     checked: false,
44655     /**
44656      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44657      * {tag: "input", type: "checkbox", autocomplete: "off"})
44658      */
44659     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44660     /**
44661      * @cfg {String} boxLabel The text that appears beside the checkbox
44662      */
44663     boxLabel : "",
44664     /**
44665      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44666      */  
44667     inputValue : '1',
44668     /**
44669      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44670      */
44671      valueOff: '0', // value when not checked..
44672
44673     actionMode : 'viewEl', 
44674     //
44675     // private
44676     itemCls : 'x-menu-check-item x-form-item',
44677     groupClass : 'x-menu-group-item',
44678     inputType : 'hidden',
44679     
44680     
44681     inSetChecked: false, // check that we are not calling self...
44682     
44683     inputElement: false, // real input element?
44684     basedOn: false, // ????
44685     
44686     isFormField: true, // not sure where this is needed!!!!
44687
44688     onResize : function(){
44689         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44690         if(!this.boxLabel){
44691             this.el.alignTo(this.wrap, 'c-c');
44692         }
44693     },
44694
44695     initEvents : function(){
44696         Roo.form.Checkbox.superclass.initEvents.call(this);
44697         this.el.on("click", this.onClick,  this);
44698         this.el.on("change", this.onClick,  this);
44699     },
44700
44701
44702     getResizeEl : function(){
44703         return this.wrap;
44704     },
44705
44706     getPositionEl : function(){
44707         return this.wrap;
44708     },
44709
44710     // private
44711     onRender : function(ct, position){
44712         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44713         /*
44714         if(this.inputValue !== undefined){
44715             this.el.dom.value = this.inputValue;
44716         }
44717         */
44718         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44719         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44720         var viewEl = this.wrap.createChild({ 
44721             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44722         this.viewEl = viewEl;   
44723         this.wrap.on('click', this.onClick,  this); 
44724         
44725         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44726         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44727         
44728         
44729         
44730         if(this.boxLabel){
44731             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44732         //    viewEl.on('click', this.onClick,  this); 
44733         }
44734         //if(this.checked){
44735             this.setChecked(this.checked);
44736         //}else{
44737             //this.checked = this.el.dom;
44738         //}
44739
44740     },
44741
44742     // private
44743     initValue : Roo.emptyFn,
44744
44745     /**
44746      * Returns the checked state of the checkbox.
44747      * @return {Boolean} True if checked, else false
44748      */
44749     getValue : function(){
44750         if(this.el){
44751             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44752         }
44753         return this.valueOff;
44754         
44755     },
44756
44757         // private
44758     onClick : function(){ 
44759         if (this.disabled) {
44760             return;
44761         }
44762         this.setChecked(!this.checked);
44763
44764         //if(this.el.dom.checked != this.checked){
44765         //    this.setValue(this.el.dom.checked);
44766        // }
44767     },
44768
44769     /**
44770      * Sets the checked state of the checkbox.
44771      * On is always based on a string comparison between inputValue and the param.
44772      * @param {Boolean/String} value - the value to set 
44773      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44774      */
44775     setValue : function(v,suppressEvent){
44776         
44777         
44778         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44779         //if(this.el && this.el.dom){
44780         //    this.el.dom.checked = this.checked;
44781         //    this.el.dom.defaultChecked = this.checked;
44782         //}
44783         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44784         //this.fireEvent("check", this, this.checked);
44785     },
44786     // private..
44787     setChecked : function(state,suppressEvent)
44788     {
44789         if (this.inSetChecked) {
44790             this.checked = state;
44791             return;
44792         }
44793         
44794     
44795         if(this.wrap){
44796             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44797         }
44798         this.checked = state;
44799         if(suppressEvent !== true){
44800             this.fireEvent('check', this, state);
44801         }
44802         this.inSetChecked = true;
44803         this.el.dom.value = state ? this.inputValue : this.valueOff;
44804         this.inSetChecked = false;
44805         
44806     },
44807     // handle setting of hidden value by some other method!!?!?
44808     setFromHidden: function()
44809     {
44810         if(!this.el){
44811             return;
44812         }
44813         //console.log("SET FROM HIDDEN");
44814         //alert('setFrom hidden');
44815         this.setValue(this.el.dom.value);
44816     },
44817     
44818     onDestroy : function()
44819     {
44820         if(this.viewEl){
44821             Roo.get(this.viewEl).remove();
44822         }
44823          
44824         Roo.form.Checkbox.superclass.onDestroy.call(this);
44825     },
44826     
44827     setBoxLabel : function(str)
44828     {
44829         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44830     }
44831
44832 });/*
44833  * Based on:
44834  * Ext JS Library 1.1.1
44835  * Copyright(c) 2006-2007, Ext JS, LLC.
44836  *
44837  * Originally Released Under LGPL - original licence link has changed is not relivant.
44838  *
44839  * Fork - LGPL
44840  * <script type="text/javascript">
44841  */
44842  
44843 /**
44844  * @class Roo.form.Radio
44845  * @extends Roo.form.Checkbox
44846  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44847  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44848  * @constructor
44849  * Creates a new Radio
44850  * @param {Object} config Configuration options
44851  */
44852 Roo.form.Radio = function(){
44853     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44854 };
44855 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44856     inputType: 'radio',
44857
44858     /**
44859      * If this radio is part of a group, it will return the selected value
44860      * @return {String}
44861      */
44862     getGroupValue : function(){
44863         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44864     },
44865     
44866     
44867     onRender : function(ct, position){
44868         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44869         
44870         if(this.inputValue !== undefined){
44871             this.el.dom.value = this.inputValue;
44872         }
44873          
44874         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44875         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44876         //var viewEl = this.wrap.createChild({ 
44877         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44878         //this.viewEl = viewEl;   
44879         //this.wrap.on('click', this.onClick,  this); 
44880         
44881         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44882         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44883         
44884         
44885         
44886         if(this.boxLabel){
44887             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44888         //    viewEl.on('click', this.onClick,  this); 
44889         }
44890          if(this.checked){
44891             this.el.dom.checked =   'checked' ;
44892         }
44893          
44894     } 
44895     
44896     
44897 });Roo.rtf = {}; // namespace
44898 Roo.rtf.Hex = function(hex)
44899 {
44900     this.hexstr = hex;
44901 };
44902 Roo.rtf.Paragraph = function(opts)
44903 {
44904     this.content = []; ///??? is that used?
44905 };Roo.rtf.Span = function(opts)
44906 {
44907     this.value = opts.value;
44908 };
44909
44910 Roo.rtf.Group = function(parent)
44911 {
44912     // we dont want to acutally store parent - it will make debug a nightmare..
44913     this.content = [];
44914     this.cn  = [];
44915      
44916        
44917     
44918 };
44919
44920 Roo.rtf.Group.prototype = {
44921     ignorable : false,
44922     content: false,
44923     cn: false,
44924     addContent : function(node) {
44925         // could set styles...
44926         this.content.push(node);
44927     },
44928     addChild : function(cn)
44929     {
44930         this.cn.push(cn);
44931     },
44932     // only for images really...
44933     toDataURL : function()
44934     {
44935         var mimetype = false;
44936         switch(true) {
44937             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
44938                 mimetype = "image/png";
44939                 break;
44940              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
44941                 mimetype = "image/jpeg";
44942                 break;
44943             default :
44944                 return 'about:blank'; // ?? error?
44945         }
44946         
44947         
44948         var hexstring = this.content[this.content.length-1].value;
44949         
44950         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
44951             return String.fromCharCode(parseInt(a, 16));
44952         }).join(""));
44953     }
44954     
44955 };
44956 // this looks like it's normally the {rtf{ .... }}
44957 Roo.rtf.Document = function()
44958 {
44959     // we dont want to acutally store parent - it will make debug a nightmare..
44960     this.rtlch  = [];
44961     this.content = [];
44962     this.cn = [];
44963     
44964 };
44965 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
44966     addChild : function(cn)
44967     {
44968         this.cn.push(cn);
44969         switch(cn.type) {
44970             case 'rtlch': // most content seems to be inside this??
44971             case 'listtext':
44972             case 'shpinst':
44973                 this.rtlch.push(cn);
44974                 return;
44975             default:
44976                 this[cn.type] = cn;
44977         }
44978         
44979     },
44980     
44981     getElementsByType : function(type)
44982     {
44983         var ret =  [];
44984         this._getElementsByType(type, ret, this.cn, 'rtf');
44985         return ret;
44986     },
44987     _getElementsByType : function (type, ret, search_array, path)
44988     {
44989         search_array.forEach(function(n,i) {
44990             if (n.type == type) {
44991                 n.path = path + '/' + n.type + ':' + i;
44992                 ret.push(n);
44993             }
44994             if (n.cn.length > 0) {
44995                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
44996             }
44997         },this);
44998     }
44999     
45000 });
45001  
45002 Roo.rtf.Ctrl = function(opts)
45003 {
45004     this.value = opts.value;
45005     this.param = opts.param;
45006 };
45007 /**
45008  *
45009  *
45010  * based on this https://github.com/iarna/rtf-parser
45011  * it's really only designed to extract pict from pasted RTF 
45012  *
45013  * usage:
45014  *
45015  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
45016  *  
45017  *
45018  */
45019
45020  
45021
45022
45023
45024 Roo.rtf.Parser = function(text) {
45025     //super({objectMode: true})
45026     this.text = '';
45027     this.parserState = this.parseText;
45028     
45029     // these are for interpeter...
45030     this.doc = {};
45031     ///this.parserState = this.parseTop
45032     this.groupStack = [];
45033     this.hexStore = [];
45034     this.doc = false;
45035     
45036     this.groups = []; // where we put the return.
45037     
45038     for (var ii = 0; ii < text.length; ++ii) {
45039         ++this.cpos;
45040         
45041         if (text[ii] === '\n') {
45042             ++this.row;
45043             this.col = 1;
45044         } else {
45045             ++this.col;
45046         }
45047         this.parserState(text[ii]);
45048     }
45049     
45050     
45051     
45052 };
45053 Roo.rtf.Parser.prototype = {
45054     text : '', // string being parsed..
45055     controlWord : '',
45056     controlWordParam :  '',
45057     hexChar : '',
45058     doc : false,
45059     group: false,
45060     groupStack : false,
45061     hexStore : false,
45062     
45063     
45064     cpos : 0, 
45065     row : 1, // reportin?
45066     col : 1, //
45067
45068      
45069     push : function (el)
45070     {
45071         var m = 'cmd'+ el.type;
45072         if (typeof(this[m]) == 'undefined') {
45073             Roo.log('invalid cmd:' + el.type);
45074             return;
45075         }
45076         this[m](el);
45077         //Roo.log(el);
45078     },
45079     flushHexStore : function()
45080     {
45081         if (this.hexStore.length < 1) {
45082             return;
45083         }
45084         var hexstr = this.hexStore.map(
45085             function(cmd) {
45086                 return cmd.value;
45087         }).join('');
45088         
45089         this.group.addContent( new Roo.rtf.Hex( hexstr ));
45090               
45091             
45092         this.hexStore.splice(0)
45093         
45094     },
45095     
45096     cmdgroupstart : function()
45097     {
45098         this.flushHexStore();
45099         if (this.group) {
45100             this.groupStack.push(this.group);
45101         }
45102          // parent..
45103         if (this.doc === false) {
45104             this.group = this.doc = new Roo.rtf.Document();
45105             return;
45106             
45107         }
45108         this.group = new Roo.rtf.Group(this.group);
45109     },
45110     cmdignorable : function()
45111     {
45112         this.flushHexStore();
45113         this.group.ignorable = true;
45114     },
45115     cmdendparagraph : function()
45116     {
45117         this.flushHexStore();
45118         this.group.addContent(new Roo.rtf.Paragraph());
45119     },
45120     cmdgroupend : function ()
45121     {
45122         this.flushHexStore();
45123         var endingGroup = this.group;
45124         
45125         
45126         this.group = this.groupStack.pop();
45127         if (this.group) {
45128             this.group.addChild(endingGroup);
45129         }
45130         
45131         
45132         
45133         var doc = this.group || this.doc;
45134         //if (endingGroup instanceof FontTable) {
45135         //  doc.fonts = endingGroup.table
45136         //} else if (endingGroup instanceof ColorTable) {
45137         //  doc.colors = endingGroup.table
45138         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
45139         if (endingGroup.ignorable === false) {
45140             //code
45141             this.groups.push(endingGroup);
45142            // Roo.log( endingGroup );
45143         }
45144             //Roo.each(endingGroup.content, function(item)) {
45145             //    doc.addContent(item);
45146             //}
45147             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
45148         //}
45149     },
45150     cmdtext : function (cmd)
45151     {
45152         this.flushHexStore();
45153         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
45154             //this.group = this.doc
45155         }
45156         this.group.addContent(new Roo.rtf.Span(cmd));
45157     },
45158     cmdcontrolword : function (cmd)
45159     {
45160         this.flushHexStore();
45161         if (!this.group.type) {
45162             this.group.type = cmd.value;
45163             return;
45164         }
45165         this.group.addContent(new Roo.rtf.Ctrl(cmd));
45166         // we actually don't care about ctrl words...
45167         return ;
45168         /*
45169         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
45170         if (this[method]) {
45171             this[method](cmd.param)
45172         } else {
45173             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
45174         }
45175         */
45176     },
45177     cmdhexchar : function(cmd) {
45178         this.hexStore.push(cmd);
45179     },
45180     cmderror : function(cmd) {
45181         throw new Exception (cmd.value);
45182     },
45183     
45184     /*
45185       _flush (done) {
45186         if (this.text !== '\u0000') this.emitText()
45187         done()
45188       }
45189       */
45190       
45191       
45192     parseText : function(c)
45193     {
45194         if (c === '\\') {
45195             this.parserState = this.parseEscapes;
45196         } else if (c === '{') {
45197             this.emitStartGroup();
45198         } else if (c === '}') {
45199             this.emitEndGroup();
45200         } else if (c === '\x0A' || c === '\x0D') {
45201             // cr/lf are noise chars
45202         } else {
45203             this.text += c;
45204         }
45205     },
45206     
45207     parseEscapes: function (c)
45208     {
45209         if (c === '\\' || c === '{' || c === '}') {
45210             this.text += c;
45211             this.parserState = this.parseText;
45212         } else {
45213             this.parserState = this.parseControlSymbol;
45214             this.parseControlSymbol(c);
45215         }
45216     },
45217     parseControlSymbol: function(c)
45218     {
45219         if (c === '~') {
45220             this.text += '\u00a0'; // nbsp
45221             this.parserState = this.parseText
45222         } else if (c === '-') {
45223              this.text += '\u00ad'; // soft hyphen
45224         } else if (c === '_') {
45225             this.text += '\u2011'; // non-breaking hyphen
45226         } else if (c === '*') {
45227             this.emitIgnorable();
45228             this.parserState = this.parseText;
45229         } else if (c === "'") {
45230             this.parserState = this.parseHexChar;
45231         } else if (c === '|') { // formula cacter
45232             this.emitFormula();
45233             this.parserState = this.parseText;
45234         } else if (c === ':') { // subentry in an index entry
45235             this.emitIndexSubEntry();
45236             this.parserState = this.parseText;
45237         } else if (c === '\x0a') {
45238             this.emitEndParagraph();
45239             this.parserState = this.parseText;
45240         } else if (c === '\x0d') {
45241             this.emitEndParagraph();
45242             this.parserState = this.parseText;
45243         } else {
45244             this.parserState = this.parseControlWord;
45245             this.parseControlWord(c);
45246         }
45247     },
45248     parseHexChar: function (c)
45249     {
45250         if (/^[A-Fa-f0-9]$/.test(c)) {
45251             this.hexChar += c;
45252             if (this.hexChar.length >= 2) {
45253               this.emitHexChar();
45254               this.parserState = this.parseText;
45255             }
45256             return;
45257         }
45258         this.emitError("Invalid character \"" + c + "\" in hex literal.");
45259         this.parserState = this.parseText;
45260         
45261     },
45262     parseControlWord : function(c)
45263     {
45264         if (c === ' ') {
45265             this.emitControlWord();
45266             this.parserState = this.parseText;
45267         } else if (/^[-\d]$/.test(c)) {
45268             this.parserState = this.parseControlWordParam;
45269             this.controlWordParam += c;
45270         } else if (/^[A-Za-z]$/.test(c)) {
45271           this.controlWord += c;
45272         } else {
45273           this.emitControlWord();
45274           this.parserState = this.parseText;
45275           this.parseText(c);
45276         }
45277     },
45278     parseControlWordParam : function (c) {
45279         if (/^\d$/.test(c)) {
45280           this.controlWordParam += c;
45281         } else if (c === ' ') {
45282           this.emitControlWord();
45283           this.parserState = this.parseText;
45284         } else {
45285           this.emitControlWord();
45286           this.parserState = this.parseText;
45287           this.parseText(c);
45288         }
45289     },
45290     
45291     
45292     
45293     
45294     emitText : function () {
45295         if (this.text === '') {
45296             return;
45297         }
45298         this.push({
45299             type: 'text',
45300             value: this.text,
45301             pos: this.cpos,
45302             row: this.row,
45303             col: this.col
45304         });
45305         this.text = ''
45306     },
45307     emitControlWord : function ()
45308     {
45309         this.emitText();
45310         if (this.controlWord === '') {
45311             this.emitError('empty control word');
45312         } else {
45313             this.push({
45314                   type: 'controlword',
45315                   value: this.controlWord,
45316                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
45317                   pos: this.cpos,
45318                   row: this.row,
45319                   col: this.col
45320             });
45321         }
45322         this.controlWord = '';
45323         this.controlWordParam = '';
45324     },
45325     emitStartGroup : function ()
45326     {
45327         this.emitText();
45328         this.push({
45329             type: 'groupstart',
45330             pos: this.cpos,
45331             row: this.row,
45332             col: this.col
45333         });
45334     },
45335     emitEndGroup : function ()
45336     {
45337         this.emitText();
45338         this.push({
45339             type: 'groupend',
45340             pos: this.cpos,
45341             row: this.row,
45342             col: this.col
45343         });
45344     },
45345     emitIgnorable : function ()
45346     {
45347         this.emitText();
45348         this.push({
45349             type: 'ignorable',
45350             pos: this.cpos,
45351             row: this.row,
45352             col: this.col
45353         });
45354     },
45355     emitHexChar : function ()
45356     {
45357         this.emitText();
45358         this.push({
45359             type: 'hexchar',
45360             value: this.hexChar,
45361             pos: this.cpos,
45362             row: this.row,
45363             col: this.col
45364         });
45365         this.hexChar = ''
45366     },
45367     emitError : function (message)
45368     {
45369       this.emitText();
45370       this.push({
45371             type: 'error',
45372             value: message,
45373             row: this.row,
45374             col: this.col,
45375             char: this.cpos //,
45376             //stack: new Error().stack
45377         });
45378     },
45379     emitEndParagraph : function () {
45380         this.emitText();
45381         this.push({
45382             type: 'endparagraph',
45383             pos: this.cpos,
45384             row: this.row,
45385             col: this.col
45386         });
45387     }
45388      
45389 } ;
45390 Roo.htmleditor = {};
45391  
45392 /**
45393  * @class Roo.htmleditor.Filter
45394  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45395  * @cfg {DomElement} node The node to iterate and filter
45396  * @cfg {boolean|String|Array} tag Tags to replace 
45397  * @constructor
45398  * Create a new Filter.
45399  * @param {Object} config Configuration options
45400  */
45401
45402
45403
45404 Roo.htmleditor.Filter = function(cfg) {
45405     Roo.apply(this.cfg);
45406     // this does not actually call walk as it's really just a abstract class
45407 }
45408
45409
45410 Roo.htmleditor.Filter.prototype = {
45411     
45412     node: false,
45413     
45414     tag: false,
45415
45416     // overrride to do replace comments.
45417     replaceComment : false,
45418     
45419     // overrride to do replace or do stuff with tags..
45420     replaceTag : false,
45421     
45422     walk : function(dom)
45423     {
45424         Roo.each( Array.from(dom.childNodes), function( e ) {
45425             switch(true) {
45426                 
45427                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
45428                     this.replaceComment(e);
45429                     return;
45430                 
45431                 case e.nodeType != 1: //not a node.
45432                     return;
45433                 
45434                 case this.tag === true: // everything
45435                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45436                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45437                     if (this.replaceTag && false === this.replaceTag(e)) {
45438                         return;
45439                     }
45440                     if (e.hasChildNodes()) {
45441                         this.walk(e);
45442                     }
45443                     return;
45444                 
45445                 default:    // tags .. that do not match.
45446                     if (e.hasChildNodes()) {
45447                         this.walk(e);
45448                     }
45449             }
45450             
45451         }, this);
45452         
45453     }
45454 }; 
45455
45456 /**
45457  * @class Roo.htmleditor.FilterAttributes
45458  * clean attributes and  styles including http:// etc.. in attribute
45459  * @constructor
45460 * Run a new Attribute Filter
45461 * @param {Object} config Configuration options
45462  */
45463 Roo.htmleditor.FilterAttributes = function(cfg)
45464 {
45465     Roo.apply(this, cfg);
45466     this.attrib_black = this.attrib_black || [];
45467     this.attrib_white = this.attrib_white || [];
45468
45469     this.attrib_clean = this.attrib_clean || [];
45470     this.style_white = this.style_white || [];
45471     this.style_black = this.style_black || [];
45472     this.walk(cfg.node);
45473 }
45474
45475 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45476 {
45477     tag: true, // all tags
45478     
45479     attrib_black : false, // array
45480     attrib_clean : false,
45481     attrib_white : false,
45482
45483     style_white : false,
45484     style_black : false,
45485      
45486      
45487     replaceTag : function(node)
45488     {
45489         if (!node.attributes || !node.attributes.length) {
45490             return true;
45491         }
45492         
45493         for (var i = node.attributes.length-1; i > -1 ; i--) {
45494             var a = node.attributes[i];
45495             //console.log(a);
45496             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45497                 node.removeAttribute(a.name);
45498                 continue;
45499             }
45500             
45501             
45502             
45503             if (a.name.toLowerCase().substr(0,2)=='on')  {
45504                 node.removeAttribute(a.name);
45505                 continue;
45506             }
45507             
45508             
45509             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45510                 node.removeAttribute(a.name);
45511                 continue;
45512             }
45513             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45514                 this.cleanAttr(node,a.name,a.value); // fixme..
45515                 continue;
45516             }
45517             if (a.name == 'style') {
45518                 this.cleanStyle(node,a.name,a.value);
45519                 continue;
45520             }
45521             /// clean up MS crap..
45522             // tecnically this should be a list of valid class'es..
45523             
45524             
45525             if (a.name == 'class') {
45526                 if (a.value.match(/^Mso/)) {
45527                     node.removeAttribute('class');
45528                 }
45529                 
45530                 if (a.value.match(/^body$/)) {
45531                     node.removeAttribute('class');
45532                 }
45533                 continue;
45534             }
45535             
45536             
45537             // style cleanup!?
45538             // class cleanup?
45539             
45540         }
45541         return true; // clean children
45542     },
45543         
45544     cleanAttr: function(node, n,v)
45545     {
45546         
45547         if (v.match(/^\./) || v.match(/^\//)) {
45548             return;
45549         }
45550         if (v.match(/^(http|https):\/\//)
45551             || v.match(/^mailto:/) 
45552             || v.match(/^ftp:/)
45553             || v.match(/^data:/)
45554             ) {
45555             return;
45556         }
45557         if (v.match(/^#/)) {
45558             return;
45559         }
45560         if (v.match(/^\{/)) { // allow template editing.
45561             return;
45562         }
45563 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45564         node.removeAttribute(n);
45565         
45566     },
45567     cleanStyle : function(node,  n,v)
45568     {
45569         if (v.match(/expression/)) { //XSS?? should we even bother..
45570             node.removeAttribute(n);
45571             return;
45572         }
45573         
45574         var parts = v.split(/;/);
45575         var clean = [];
45576         
45577         Roo.each(parts, function(p) {
45578             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45579             if (!p.length) {
45580                 return true;
45581             }
45582             var l = p.split(':').shift().replace(/\s+/g,'');
45583             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45584             
45585             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45586                 return true;
45587             }
45588             //Roo.log()
45589             // only allow 'c whitelisted system attributes'
45590             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45591                 return true;
45592             }
45593             
45594             
45595             clean.push(p);
45596             return true;
45597         },this);
45598         if (clean.length) { 
45599             node.setAttribute(n, clean.join(';'));
45600         } else {
45601             node.removeAttribute(n);
45602         }
45603         
45604     }
45605         
45606         
45607         
45608     
45609 });/**
45610  * @class Roo.htmleditor.FilterBlack
45611  * remove blacklisted elements.
45612  * @constructor
45613  * Run a new Blacklisted Filter
45614  * @param {Object} config Configuration options
45615  */
45616
45617 Roo.htmleditor.FilterBlack = function(cfg)
45618 {
45619     Roo.apply(this, cfg);
45620     this.walk(cfg.node);
45621 }
45622
45623 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45624 {
45625     tag : true, // all elements.
45626    
45627     replace : function(n)
45628     {
45629         n.parentNode.removeChild(n);
45630     }
45631 });
45632 /**
45633  * @class Roo.htmleditor.FilterComment
45634  * remove comments.
45635  * @constructor
45636 * Run a new Comments Filter
45637 * @param {Object} config Configuration options
45638  */
45639 Roo.htmleditor.FilterComment = function(cfg)
45640 {
45641     this.walk(cfg.node);
45642 }
45643
45644 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45645 {
45646   
45647     replaceComment : function(n)
45648     {
45649         n.parentNode.removeChild(n);
45650     }
45651 });/**
45652  * @class Roo.htmleditor.FilterKeepChildren
45653  * remove tags but keep children
45654  * @constructor
45655  * Run a new Keep Children Filter
45656  * @param {Object} config Configuration options
45657  */
45658
45659 Roo.htmleditor.FilterKeepChildren = function(cfg)
45660 {
45661     Roo.apply(this, cfg);
45662     if (this.tag === false) {
45663         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45664     }
45665     this.walk(cfg.node);
45666 }
45667
45668 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45669 {
45670     
45671   
45672     replaceTag : function(node)
45673     {
45674         // walk children...
45675         //Roo.log(node);
45676         var ar = Array.from(node.childNodes);
45677         //remove first..
45678         for (var i = 0; i < ar.length; i++) {
45679             if (ar[i].nodeType == 1) {
45680                 if (
45681                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45682                     || // array and it matches
45683                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45684                 ) {
45685                     this.replaceTag(ar[i]); // child is blacklisted as well...
45686                     continue;
45687                 }
45688             }
45689         }  
45690         ar = Array.from(node.childNodes);
45691         for (var i = 0; i < ar.length; i++) {
45692          
45693             node.removeChild(ar[i]);
45694             // what if we need to walk these???
45695             node.parentNode.insertBefore(ar[i], node);
45696             if (this.tag !== false) {
45697                 this.walk(ar[i]);
45698                 
45699             }
45700         }
45701         node.parentNode.removeChild(node);
45702         return false; // don't walk children
45703         
45704         
45705     }
45706 });/**
45707  * @class Roo.htmleditor.FilterParagraph
45708  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45709  * like on 'push' to remove the <p> tags and replace them with line breaks.
45710  * @constructor
45711  * Run a new Paragraph Filter
45712  * @param {Object} config Configuration options
45713  */
45714
45715 Roo.htmleditor.FilterParagraph = function(cfg)
45716 {
45717     // no need to apply config.
45718     this.walk(cfg.node);
45719 }
45720
45721 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45722 {
45723     
45724      
45725     tag : 'P',
45726     
45727      
45728     replaceTag : function(node)
45729     {
45730         
45731         if (node.childNodes.length == 1 &&
45732             node.childNodes[0].nodeType == 3 &&
45733             node.childNodes[0].textContent.trim().length < 1
45734             ) {
45735             // remove and replace with '<BR>';
45736             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45737             return false; // no need to walk..
45738         }
45739         var ar = Array.from(node.childNodes);
45740         for (var i = 0; i < ar.length; i++) {
45741             node.removeChild(ar[i]);
45742             // what if we need to walk these???
45743             node.parentNode.insertBefore(ar[i], node);
45744         }
45745         // now what about this?
45746         // <p> &nbsp; </p>
45747         
45748         // double BR.
45749         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45750         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45751         node.parentNode.removeChild(node);
45752         
45753         return false;
45754
45755     }
45756     
45757 });/**
45758  * @class Roo.htmleditor.FilterSpan
45759  * filter span's with no attributes out..
45760  * @constructor
45761  * Run a new Span Filter
45762  * @param {Object} config Configuration options
45763  */
45764
45765 Roo.htmleditor.FilterSpan = function(cfg)
45766 {
45767     // no need to apply config.
45768     this.walk(cfg.node);
45769 }
45770
45771 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45772 {
45773      
45774     tag : 'SPAN',
45775      
45776  
45777     replaceTag : function(node)
45778     {
45779         if (node.attributes && node.attributes.length > 0) {
45780             return true; // walk if there are any.
45781         }
45782         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
45783         return false;
45784      
45785     }
45786     
45787 });/**
45788  * @class Roo.htmleditor.FilterTableWidth
45789   try and remove table width data - as that frequently messes up other stuff.
45790  * 
45791  *      was cleanTableWidths.
45792  *
45793  * Quite often pasting from word etc.. results in tables with column and widths.
45794  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45795  *
45796  * @constructor
45797  * Run a new Table Filter
45798  * @param {Object} config Configuration options
45799  */
45800
45801 Roo.htmleditor.FilterTableWidth = function(cfg)
45802 {
45803     // no need to apply config.
45804     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
45805     this.walk(cfg.node);
45806 }
45807
45808 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
45809 {
45810      
45811      
45812     
45813     replaceTag: function(node) {
45814         
45815         
45816       
45817         if (node.hasAttribute('width')) {
45818             node.removeAttribute('width');
45819         }
45820         
45821          
45822         if (node.hasAttribute("style")) {
45823             // pretty basic...
45824             
45825             var styles = node.getAttribute("style").split(";");
45826             var nstyle = [];
45827             Roo.each(styles, function(s) {
45828                 if (!s.match(/:/)) {
45829                     return;
45830                 }
45831                 var kv = s.split(":");
45832                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45833                     return;
45834                 }
45835                 // what ever is left... we allow.
45836                 nstyle.push(s);
45837             });
45838             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45839             if (!nstyle.length) {
45840                 node.removeAttribute('style');
45841             }
45842         }
45843         
45844         return true; // continue doing children..
45845     }
45846 });/**
45847  * @class Roo.htmleditor.FilterWord
45848  * try and clean up all the mess that Word generates.
45849  * 
45850  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
45851  
45852  * @constructor
45853  * Run a new Span Filter
45854  * @param {Object} config Configuration options
45855  */
45856
45857 Roo.htmleditor.FilterWord = function(cfg)
45858 {
45859     // no need to apply config.
45860     this.walk(cfg.node);
45861 }
45862
45863 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
45864 {
45865     tag: true,
45866      
45867     
45868     /**
45869      * Clean up MS wordisms...
45870      */
45871     replaceTag : function(node)
45872     {
45873          
45874         // no idea what this does - span with text, replaceds with just text.
45875         if(
45876                 node.nodeName == 'SPAN' &&
45877                 !node.hasAttributes() &&
45878                 node.childNodes.length == 1 &&
45879                 node.firstChild.nodeName == "#text"  
45880         ) {
45881             var textNode = node.firstChild;
45882             node.removeChild(textNode);
45883             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45884                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
45885             }
45886             node.parentNode.insertBefore(textNode, node);
45887             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45888                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
45889             }
45890             
45891             node.parentNode.removeChild(node);
45892             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
45893         }
45894         
45895    
45896         
45897         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
45898             node.parentNode.removeChild(node);
45899             return false; // dont do chidlren
45900         }
45901         //Roo.log(node.tagName);
45902         // remove - but keep children..
45903         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
45904             //Roo.log('-- removed');
45905             while (node.childNodes.length) {
45906                 var cn = node.childNodes[0];
45907                 node.removeChild(cn);
45908                 node.parentNode.insertBefore(cn, node);
45909                 // move node to parent - and clean it..
45910                 this.replaceTag(cn);
45911             }
45912             node.parentNode.removeChild(node);
45913             /// no need to iterate chidlren = it's got none..
45914             //this.iterateChildren(node, this.cleanWord);
45915             return false; // no need to iterate children.
45916         }
45917         // clean styles
45918         if (node.className.length) {
45919             
45920             var cn = node.className.split(/\W+/);
45921             var cna = [];
45922             Roo.each(cn, function(cls) {
45923                 if (cls.match(/Mso[a-zA-Z]+/)) {
45924                     return;
45925                 }
45926                 cna.push(cls);
45927             });
45928             node.className = cna.length ? cna.join(' ') : '';
45929             if (!cna.length) {
45930                 node.removeAttribute("class");
45931             }
45932         }
45933         
45934         if (node.hasAttribute("lang")) {
45935             node.removeAttribute("lang");
45936         }
45937         
45938         if (node.hasAttribute("style")) {
45939             
45940             var styles = node.getAttribute("style").split(";");
45941             var nstyle = [];
45942             Roo.each(styles, function(s) {
45943                 if (!s.match(/:/)) {
45944                     return;
45945                 }
45946                 var kv = s.split(":");
45947                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45948                     return;
45949                 }
45950                 // what ever is left... we allow.
45951                 nstyle.push(s);
45952             });
45953             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45954             if (!nstyle.length) {
45955                 node.removeAttribute('style');
45956             }
45957         }
45958         return true; // do children
45959         
45960         
45961         
45962     }
45963 });
45964 /**
45965  * @class Roo.htmleditor.FilterStyleToTag
45966  * part of the word stuff... - certain 'styles' should be converted to tags.
45967  * eg.
45968  *   font-weight: bold -> bold
45969  *   ?? super / subscrit etc..
45970  * 
45971  * @constructor
45972 * Run a new style to tag filter.
45973 * @param {Object} config Configuration options
45974  */
45975 Roo.htmleditor.FilterStyleToTag = function(cfg)
45976 {
45977     
45978     this.tags = {
45979         B  : [ 'fontWeight' , 'bold'],
45980         I :  [ 'fontStyle' , 'italic'],
45981         //pre :  [ 'font-style' , 'italic'],
45982         // h1.. h6 ?? font-size?
45983         SUP : [ 'verticalAlign' , 'super' ],
45984         SUB : [ 'verticalAlign' , 'sub' ]
45985         
45986         
45987     };
45988     
45989     Roo.apply(this, cfg);
45990      
45991     
45992     this.walk(cfg.node);
45993     
45994     
45995     
45996 }
45997
45998
45999 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
46000 {
46001     tag: true, // all tags
46002     
46003     tags : false,
46004     
46005     
46006     replaceTag : function(node)
46007     {
46008         
46009         
46010         if (node.getAttribute("style") === null) {
46011             return true;
46012         }
46013         var inject = [];
46014         for (var k in this.tags) {
46015             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
46016                 inject.push(k);
46017                 node.style.removeProperty(this.tags[k][0]);
46018             }
46019         }
46020         if (!inject.length) {
46021             return true; 
46022         }
46023         var cn = Array.from(node.childNodes);
46024         var nn = node;
46025         Roo.each(inject, function(t) {
46026             var nc = node.ownerDocument.createelement(t);
46027             nn.appendChild(nc);
46028             nn = nc;
46029         });
46030         for(var i = 0;i < cn.length;cn++) {
46031             node.removeChild(cn[i]);
46032             nn.appendChild(cn[i]);
46033         }
46034         return true /// iterate thru
46035     }
46036     
46037 })/**
46038  * @class Roo.htmleditor.FilterLongBr
46039  * BR/BR/BR - keep a maximum of 2...
46040  * @constructor
46041  * Run a new Long BR Filter
46042  * @param {Object} config Configuration options
46043  */
46044
46045 Roo.htmleditor.FilterLongBr = function(cfg)
46046 {
46047     // no need to apply config.
46048     this.walk(cfg.node);
46049 }
46050
46051 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
46052 {
46053     
46054      
46055     tag : 'BR',
46056     
46057      
46058     replaceTag : function(node)
46059     {
46060         
46061         var ps = node.nextSibling;
46062         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46063             ps = ps.nextSibling;
46064         }
46065         
46066         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
46067             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
46068             return false;
46069         }
46070         
46071         if (!ps || ps.nodeType != 1) {
46072             return false;
46073         }
46074         
46075         if (!ps || ps.tagName != 'BR') {
46076            
46077             return false;
46078         }
46079         
46080         
46081         
46082         
46083         
46084         if (!node.previousSibling) {
46085             return false;
46086         }
46087         var ps = node.previousSibling;
46088         
46089         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46090             ps = ps.previousSibling;
46091         }
46092         if (!ps || ps.nodeType != 1) {
46093             return false;
46094         }
46095         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
46096         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
46097             return false;
46098         }
46099         
46100         node.parentNode.removeChild(node); // remove me...
46101         
46102         return false; // no need to do children
46103
46104     }
46105     
46106 }); 
46107
46108 /**
46109  * @class Roo.htmleditor.FilterBlock
46110  * removes id / data-block and contenteditable that are associated with blocks
46111  * usage should be done on a cloned copy of the dom
46112  * @constructor
46113 * Run a new Attribute Filter { node : xxxx }}
46114 * @param {Object} config Configuration options
46115  */
46116 Roo.htmleditor.FilterBlock = function(cfg)
46117 {
46118     Roo.apply(this, cfg);
46119     var qa = cfg.node.querySelectorAll;
46120     this.removeAttributes('data-block');
46121     this.removeAttributes('contenteditable');
46122     this.removeAttributes('id');
46123     
46124 }
46125
46126 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
46127 {
46128     node: true, // all tags
46129      
46130      
46131     removeAttributes : function(attr)
46132     {
46133         var ar = this.node.querySelectorAll('*[' + attr + ']');
46134         for (var i =0;i<ar.length;i++) {
46135             ar[i].removeAttribute(attr);
46136         }
46137     }
46138         
46139         
46140         
46141     
46142 });
46143 /**
46144  * @class Roo.htmleditor.Tidy
46145  * Tidy HTML 
46146  * @cfg {Roo.HtmlEditorCore} core the editor.
46147  * @constructor
46148  * Create a new Filter.
46149  * @param {Object} config Configuration options
46150  */
46151
46152
46153 Roo.htmleditor.Tidy = function(cfg) {
46154     Roo.apply(this, cfg);
46155     
46156     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
46157      
46158 }
46159
46160 Roo.htmleditor.Tidy.toString = function(node)
46161 {
46162     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
46163 }
46164
46165 Roo.htmleditor.Tidy.prototype = {
46166     
46167     
46168     wrap : function(s) {
46169         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
46170     },
46171
46172     
46173     tidy : function(node, indent) {
46174      
46175         if  (node.nodeType == 3) {
46176             // text.
46177             
46178             
46179             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
46180                 
46181             
46182         }
46183         
46184         if  (node.nodeType != 1) {
46185             return '';
46186         }
46187         
46188         
46189         
46190         if (node.tagName == 'BODY') {
46191             
46192             return this.cn(node, '');
46193         }
46194              
46195              // Prints the node tagName, such as <A>, <IMG>, etc
46196         var ret = "<" + node.tagName +  this.attr(node) ;
46197         
46198         // elements with no children..
46199         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
46200                 return ret + '/>';
46201         }
46202         ret += '>';
46203         
46204         
46205         var cindent = indent === false ? '' : (indent + '  ');
46206         // tags where we will not pad the children.. (inline text tags etc..)
46207         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
46208             cindent = false;
46209             
46210             
46211         }
46212         
46213         var cn = this.cn(node, cindent );
46214         
46215         return ret + cn  + '</' + node.tagName + '>';
46216         
46217     },
46218     cn: function(node, indent)
46219     {
46220         var ret = [];
46221         
46222         var ar = Array.from(node.childNodes);
46223         for (var i = 0 ; i < ar.length ; i++) {
46224             
46225             
46226             
46227             if (indent !== false   // indent==false preservies everything
46228                 && i > 0
46229                 && ar[i].nodeType == 3 
46230                 && ar[i].nodeValue.length > 0
46231                 && ar[i].nodeValue.match(/^\s+/)
46232             ) {
46233                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
46234                     ret.pop(); // remove line break from last?
46235                 }
46236                 
46237                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
46238             }
46239             if (indent !== false
46240                 && ar[i].nodeType == 1 // element - and indent is not set... 
46241             ) {
46242                 ret.push("\n" + indent); 
46243             }
46244             
46245             ret.push(this.tidy(ar[i], indent));
46246             // text + trailing indent 
46247             if (indent !== false
46248                 && ar[i].nodeType == 3
46249                 && ar[i].nodeValue.length > 0
46250                 && ar[i].nodeValue.match(/\s+$/)
46251             ){
46252                 ret.push("\n" + indent); 
46253             }
46254             
46255             
46256             
46257             
46258         }
46259         // what if all text?
46260         
46261         
46262         return ret.join('');
46263     },
46264     
46265          
46266         
46267     attr : function(node)
46268     {
46269         var attr = [];
46270         for(i = 0; i < node.attributes.length;i++) {
46271             
46272             // skip empty values?
46273             if (!node.attributes.item(i).value.length) {
46274                 continue;
46275             }
46276             attr.push(  node.attributes.item(i).name + '="' +
46277                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
46278             );
46279         }
46280         return attr.length ? (' ' + attr.join(' ') ) : '';
46281         
46282     }
46283     
46284     
46285     
46286 }
46287 /**
46288  * @class Roo.htmleditor.KeyEnter
46289  * Handle Enter press..
46290  * @cfg {Roo.HtmlEditorCore} core the editor.
46291  * @constructor
46292  * Create a new Filter.
46293  * @param {Object} config Configuration options
46294  */
46295
46296
46297
46298 Roo.htmleditor.KeyEnter = function(cfg) {
46299     Roo.apply(this, cfg);
46300     // this does not actually call walk as it's really just a abstract class
46301  
46302     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
46303 }
46304
46305 //Roo.htmleditor.KeyEnter.i = 0;
46306
46307
46308 Roo.htmleditor.KeyEnter.prototype = {
46309     
46310     core : false,
46311     
46312     keypress : function(e)
46313     {
46314         if (e.charCode != 13) {
46315             return true;
46316         }
46317         e.preventDefault();
46318         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
46319         var doc = this.core.doc;
46320         
46321         var docFragment = doc.createDocumentFragment();
46322     
46323         //add a new line
46324        
46325     
46326     
46327         var range = this.core.win.getSelection().getRangeAt(0);
46328         var n = range.commonAncestorContainer ;
46329         while (n && n.nodeType != 1) {
46330             n  = n.parentNode;
46331         }
46332         var li = false;
46333         if (n && n.tagName == 'UL') {
46334             li = doc.createElement('LI');
46335             n.appendChild(li);
46336             
46337         }
46338         if (n && n.tagName == 'LI') {
46339             li = doc.createElement('LI');
46340             if (n.nextSibling) {
46341                 n.parentNode.insertBefore(li, n.firstSibling);
46342                 
46343             } else {
46344                 n.parentNode.appendChild(li);
46345             }
46346         }
46347         if (li) {   
46348             range = doc.createRange();
46349             range.setStartAfter(li);
46350             range.collapse(true);
46351         
46352             //make the cursor there
46353             var sel = this.core.win.getSelection();
46354             sel.removeAllRanges();
46355             sel.addRange(range);
46356             this.core.undoManager.addEvent();
46357             return false;
46358             
46359             
46360         }
46361         var newEle = doc.createTextNode('\n');
46362         docFragment.appendChild(newEle);
46363         
46364         //add the br, or p, or something else
46365         newEle = doc.createElement('br');
46366         //newEle.setAttribute('data-id', Roo.htmleditor.KeyEnter.i++);
46367         docFragment.appendChild(newEle);
46368         doc.createTextNode('\n');
46369         docFragment.appendChild(newEle);
46370         
46371         range.deleteContents();
46372         range.insertNode(docFragment);  //<< inseting here...
46373          
46374         var ns = newEle.nextSibling;
46375         while (ns && ns.nodeType == 3) { 
46376             ns = ns.nextSibling;
46377         }
46378         
46379         if (!ns) {
46380             //Roo.log('add extra');
46381             ns = doc.createElement('br');
46382             //ns.setAttribute('data-id', 'x' +  Roo.htmleditor.KeyEnter.i++);
46383             newEle.parentNode.appendChild(ns);
46384         }
46385         
46386         
46387         
46388         range = doc.createRange();
46389         range.setStartAfter(newEle);
46390         range.collapse(true);
46391         
46392         var sel = this.core.win.getSelection();
46393         sel.removeAllRanges();
46394         sel.addRange(range);
46395         //this.core.undoManager.addEvent();
46396         return false;
46397          
46398     }
46399 };
46400      
46401 /**
46402  * @class Roo.htmleditor.Block
46403  * Base class for html editor blocks - do not use it directly .. extend it..
46404  * @cfg {DomElement} node The node to apply stuff to.
46405  * @cfg {String} friendly_name the name that appears in the context bar about this block
46406  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
46407  
46408  * @constructor
46409  * Create a new Filter.
46410  * @param {Object} config Configuration options
46411  */
46412
46413 Roo.htmleditor.Block  = function(cfg)
46414 {
46415     // do nothing .. should not be called really.
46416 }
46417 /**
46418  * factory method to get the block from an element (using cache if necessary)
46419  * @static
46420  * @param {HtmlElement} the dom element
46421  */
46422 Roo.htmleditor.Block.factory = function(node)
46423 {
46424     var cc = Roo.htmleditor.Block.cache;
46425     var id = Roo.get(node).id;
46426     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
46427         Roo.htmleditor.Block.cache[id].readElement();
46428         return Roo.htmleditor.Block.cache[id];
46429     }
46430     var db  = node.getAttribute('data-block');
46431     if (!db) {
46432         db = node.nodeName.toLowerCase().toUpperCaseFirst();
46433     }
46434     var cls = Roo.htmleditor['Block' + db];
46435     if (typeof(cls) == 'undefined') {
46436         //Roo.log(node.getAttribute('data-block'));
46437         Roo.log("OOps missing block : " + 'Block' + db);
46438         return false;
46439     }
46440     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
46441     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
46442 };
46443
46444 /**
46445  * initalize all Elements from content that are 'blockable'
46446  * @static
46447  * @param the body element
46448  */
46449 Roo.htmleditor.Block.initAll = function(body, type)
46450 {
46451     if (typeof(type) == 'undefined') {
46452         var ia = Roo.htmleditor.Block.initAll;
46453         ia(body,'table');
46454         ia(body,'td');
46455         ia(body,'figure');
46456         return;
46457     }
46458     Roo.each(Roo.get(body).query(type), function(e) {
46459         Roo.htmleditor.Block.factory(e);    
46460     },this);
46461 };
46462 // question goes here... do we need to clear out this cache sometimes?
46463 // or show we make it relivant to the htmleditor.
46464 Roo.htmleditor.Block.cache = {};
46465
46466 Roo.htmleditor.Block.prototype = {
46467     
46468     node : false,
46469     
46470      // used by context menu
46471     friendly_name : 'Based Block',
46472     
46473     // text for button to delete this element
46474     deleteTitle : false,
46475     
46476     context : false,
46477     /**
46478      * Update a node with values from this object
46479      * @param {DomElement} node
46480      */
46481     updateElement : function(node)
46482     {
46483         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
46484     },
46485      /**
46486      * convert to plain HTML for calling insertAtCursor..
46487      */
46488     toHTML : function()
46489     {
46490         return Roo.DomHelper.markup(this.toObject());
46491     },
46492     /**
46493      * used by readEleemnt to extract data from a node
46494      * may need improving as it's pretty basic
46495      
46496      * @param {DomElement} node
46497      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
46498      * @param {String} attribute (use html - for contents, or style for using next param as style)
46499      * @param {String} style the style property - eg. text-align
46500      */
46501     getVal : function(node, tag, attr, style)
46502     {
46503         var n = node;
46504         if (tag !== true && n.tagName != tag.toUpperCase()) {
46505             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
46506             // but kiss for now.
46507             n = node.getElementsByTagName(tag).item(0);
46508         }
46509         if (attr == 'html') {
46510             return n.innerHTML;
46511         }
46512         if (attr == 'style') {
46513             return n.style[style]
46514         }
46515         
46516         return Roo.get(n).attr(attr);
46517             
46518     },
46519     /**
46520      * create a DomHelper friendly object - for use with 
46521      * Roo.DomHelper.markup / overwrite / etc..
46522      * (override this)
46523      */
46524     toObject : function()
46525     {
46526         return {};
46527     },
46528       /**
46529      * Read a node that has a 'data-block' property - and extract the values from it.
46530      * @param {DomElement} node - the node
46531      */
46532     readElement : function(node)
46533     {
46534         
46535     } 
46536     
46537     
46538 };
46539
46540  
46541
46542 /**
46543  * @class Roo.htmleditor.BlockFigure
46544  * Block that has an image and a figcaption
46545  * @cfg {String} image_src the url for the image
46546  * @cfg {String} align (left|right) alignment for the block default left
46547  * @cfg {String} text_align (left|right) alignment for the text caption default left.
46548  * @cfg {String} caption the text to appear below  (and in the alt tag)
46549  * @cfg {String|number} image_width the width of the image number or %?
46550  * @cfg {String|number} image_height the height of the image number or %?
46551  * 
46552  * @constructor
46553  * Create a new Filter.
46554  * @param {Object} config Configuration options
46555  */
46556
46557 Roo.htmleditor.BlockFigure = function(cfg)
46558 {
46559     if (cfg.node) {
46560         this.readElement(cfg.node);
46561         this.updateElement(cfg.node);
46562     }
46563     Roo.apply(this, cfg);
46564 }
46565 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
46566  
46567     
46568     // setable values.
46569     image_src: '',
46570     
46571     align: 'left',
46572     caption : '',
46573     text_align: 'left',
46574     
46575     width : '46%',
46576     margin: '2%',
46577     
46578     // used by context menu
46579     friendly_name : 'Image with caption',
46580     deleteTitle : "Delete Image and Caption",
46581     
46582     context : { // ?? static really
46583         width : {
46584             title: "Width",
46585             width: 40
46586             // ?? number
46587         },
46588         margin : {
46589             title: "Margin",
46590             width: 40
46591             // ?? number
46592         },
46593         align: {
46594             title: "Align",
46595             opts : [[ "left"],[ "right"]],
46596             width : 80
46597             
46598         },
46599         text_align: {
46600             title: "Caption Align",
46601             opts : [ [ "left"],[ "right"],[ "center"]],
46602             width : 80
46603         },
46604         
46605        
46606         image_src : {
46607             title: "Src",
46608             width: 220
46609         }
46610     },
46611     /**
46612      * create a DomHelper friendly object - for use with
46613      * Roo.DomHelper.markup / overwrite / etc..
46614      */
46615     toObject : function()
46616     {
46617         var d = document.createElement('div');
46618         d.innerHTML = this.caption;
46619         
46620         return {
46621             tag: 'figure',
46622             'data-block' : 'Figure',
46623             contenteditable : 'false',
46624             style : {
46625                 display: 'table',
46626                 float :  this.align ,
46627                 width :  this.width,
46628                 margin:  this.margin
46629             },
46630             cn : [
46631                 {
46632                     tag : 'img',
46633                     src : this.image_src,
46634                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
46635                     style: {
46636                         width: '100%'
46637                     }
46638                 },
46639                 {
46640                     tag: 'figcaption',
46641                     contenteditable : true,
46642                     style : {
46643                         'text-align': this.text_align
46644                     },
46645                     html : this.caption
46646                     
46647                 }
46648             ]
46649         };
46650     },
46651     
46652     readElement : function(node)
46653     {
46654         this.image_src = this.getVal(node, 'img', 'src');
46655         this.align = this.getVal(node, 'figure', 'style', 'float');
46656         this.caption = this.getVal(node, 'figcaption', 'html');
46657         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
46658         this.width = this.getVal(node, 'figure', 'style', 'width');
46659         this.margin = this.getVal(node, 'figure', 'style', 'margin');
46660         
46661     } 
46662     
46663   
46664    
46665      
46666     
46667     
46668     
46669     
46670 })
46671
46672  
46673
46674 /**
46675  * @class Roo.htmleditor.BlockTable
46676  * Block that manages a table
46677  * 
46678  * @constructor
46679  * Create a new Filter.
46680  * @param {Object} config Configuration options
46681  */
46682
46683 Roo.htmleditor.BlockTable = function(cfg)
46684 {
46685     if (cfg.node) {
46686         this.readElement(cfg.node);
46687         this.updateElement(cfg.node);
46688     }
46689     Roo.apply(this, cfg);
46690     if (!cfg.node) {
46691         this.rows = [];
46692         for(var r = 0; r < this.no_row; r++) {
46693             this.rows[r] = [];
46694             for(var c = 0; c < this.no_col; c++) {
46695                 this.rows[r][c] = this.emptyCell();
46696             }
46697         }
46698     }
46699     
46700     
46701 }
46702 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
46703  
46704     rows : false,
46705     no_col : 1,
46706     no_row : 1,
46707     
46708     
46709     width: '100%',
46710     
46711     // used by context menu
46712     friendly_name : 'Table',
46713     deleteTitle : 'Delete Table',
46714     // context menu is drawn once..
46715     
46716     contextMenu : function(toolbar)
46717     {
46718         
46719         var block = function() {
46720             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
46721         };
46722         
46723         
46724         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
46725         
46726         var syncValue = toolbar.editorcore.syncValue;
46727         
46728         var fields = {};
46729         
46730         return [
46731             {
46732                 xtype : 'ComboBox',
46733                 allowBlank : false,
46734                 displayField : 'val',
46735                 editable : true,
46736                 listWidth : 100,
46737                 triggerAction : 'all',
46738                 typeAhead : true,
46739                 valueField : 'val',
46740                 width : 100,
46741                 name : 'width',
46742                 listeners : {
46743                     select : function (combo, r, index)
46744                     {
46745                         var b = block();
46746                         b.width = r.get('val');
46747                         b.updateElement();
46748                         syncValue();
46749                         
46750                     }
46751                 },
46752                 xns : rooui.form,
46753                 store : {
46754                     xtype : 'SimpleStore',
46755                     data : [
46756                         ['100%'],
46757                         ['auto']
46758                     ],
46759                     fields : [ 'val'],
46760                     xns : Roo.data
46761                 }
46762             },
46763             // -------- Cols
46764             
46765             {
46766                 xtype : 'TextItem',
46767                 text : "Columns: ",
46768                 xns : rooui.Toolbar  //Boostrap?
46769             },
46770          
46771             {
46772                 xtype : 'Button',
46773                 text: '-',
46774                 listeners : {
46775                     click : function (_self, e)
46776                     {
46777                         block().removeColumn();
46778                         syncValue();
46779                     }
46780                 },
46781                 xns : rooui.Toolbar
46782             },
46783             {
46784                 xtype : 'Button',
46785                 text: '+',
46786                 listeners : {
46787                     click : function (_self, e)
46788                     {
46789                         block().addColumn();
46790                         syncValue();
46791                     }
46792                 },
46793                 xns : rooui.Toolbar
46794             },
46795             // -------- ROWS
46796             {
46797                 xtype : 'TextItem',
46798                 text : "Rows: ",
46799                 xns : rooui.Toolbar  //Boostrap?
46800             },
46801          
46802             {
46803                 xtype : 'Button',
46804                 text: '-',
46805                 listeners : {
46806                     click : function (_self, e)
46807                     {
46808                         block().removeRow();
46809                         syncValue();
46810                     }
46811                 },
46812                 xns : rooui.Toolbar
46813             },
46814             {
46815                 xtype : 'Button',
46816                 text: '+',
46817                 listeners : {
46818                     click : function (_self, e)
46819                     {
46820                         block().addRow();
46821                         syncValue();
46822                     }
46823                 },
46824                 xns : rooui.Toolbar
46825             },
46826             // -------- ROWS
46827             {
46828                 xtype : 'Button',
46829                 text: 'Reset Column Widths',
46830                 listeners : {
46831                     
46832                     click : function (_self, e)
46833                     {
46834                         block().resetWidths();
46835                         syncValue();
46836                     }
46837                 },
46838                 xns : rooui.Toolbar
46839             } 
46840             
46841             
46842             
46843         ];
46844         
46845     },
46846     
46847     
46848   /**
46849      * create a DomHelper friendly object - for use with
46850      * Roo.DomHelper.markup / overwrite / etc..
46851      * ?? should it be called with option to hide all editing features?
46852      */
46853     toObject : function()
46854     {
46855         
46856         var ret = {
46857             tag : 'table',
46858             contenteditable : 'false', // this stops cell selection from picking the table.
46859             'data-block' : 'Table',
46860             style : {
46861                 width:  this.width,
46862                 border : 'solid 1px #000', // ??? hard coded?
46863                 'border-collapse' : 'collapse' 
46864             },
46865             cn : [
46866                 { tag : 'tbody' , cn : [] }
46867             ]
46868         };
46869         
46870         // do we have a head = not really 
46871         var ncols = 0;
46872         Roo.each(this.rows, function( row ) {
46873             var tr = {
46874                 tag: 'tr',
46875                 style : {
46876                     margin: '6px',
46877                     border : 'solid 1px #000',
46878                     textAlign : 'left' 
46879                 },
46880                 cn : [ ]
46881             };
46882             
46883             ret.cn[0].cn.push(tr);
46884             // does the row have any properties? ?? height?
46885             var nc = 0;
46886             Roo.each(row, function( cell ) {
46887                 
46888                 var td = {
46889                     tag : 'td',
46890                     contenteditable :  'true',
46891                     'data-block' : 'Td',
46892                     html : cell.html,
46893                     style : cell.style
46894                 };
46895                 if (cell.colspan > 1) {
46896                     td.colspan = cell.colspan ;
46897                     nc += cell.colspan;
46898                 } else {
46899                     nc++;
46900                 }
46901                 if (cell.rowspan > 1) {
46902                     td.rowspan = cell.rowspan ;
46903                 }
46904                 
46905                 
46906                 // widths ?
46907                 tr.cn.push(td);
46908                     
46909                 
46910             }, this);
46911             ncols = Math.max(nc, ncols);
46912             
46913             
46914         }, this);
46915         // add the header row..
46916         
46917         ncols++;
46918          
46919         
46920         return ret;
46921          
46922     },
46923     
46924     readElement : function(node)
46925     {
46926         node  = node ? node : this.node ;
46927         this.width = this.getVal(node, true, 'style', 'width') || '100%';
46928         
46929         this.rows = [];
46930         this.no_row = 0;
46931         var trs = Array.from(node.getElementsByTagName('tr'));
46932         trs.forEach(function(tr) {
46933             var row =  [];
46934             this.rows.push(row);
46935             if (Roo.get(tr).hasClass('roo-html-editor-el')) { // ??? this is for our 'row' selection'
46936                 return;
46937             }
46938             this.no_row++;
46939             var no_column = 0;
46940             Array.from(tr.getElementsByTagName('td')).forEach(function(td) {
46941                 if (Roo.get(td).hasClass('roo-html-editor-el')) { // ??? this is for our 'row' selection'
46942                     return;
46943                 }
46944                 var add = {
46945                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan') : 1,
46946                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan') : 1,
46947                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
46948                     html : td.innerHTML
46949                 };
46950                 no_column += add.colspan;
46951                      
46952                 
46953                 row.push(add);
46954                 
46955                 
46956             },this);
46957             this.no_col = Math.max(this.no_col, no_column);
46958             
46959             
46960         },this);
46961         
46962         
46963     },
46964     normalizeRows: function()
46965     {
46966         var ret= [];
46967         var rid = -1;
46968         this.rows.forEach(function(row) {
46969             rid++;
46970             ret[rid] = [];
46971             row = this.normalizeRow(row);
46972             var cid = 0;
46973             row.forEach(function(c) {
46974                 while (typeof(ret[rid][cid]) != 'undefined') {
46975                     cid++;
46976                 }
46977                 if (typeof(ret[rid]) == 'undefined') {
46978                     ret[rid] = [];
46979                 }
46980                 ret[rid][cid] = c;
46981                 c.row = rid;
46982                 c.col = cid;
46983                 if (c.rowspan < 2) {
46984                     return;
46985                 }
46986                 
46987                 for(var i = 1 ;i < c.rowspan; i++) {
46988                     if (typeof(ret[rid+i]) == 'undefined') {
46989                         ret[rid+i] = [];
46990                     }
46991                     ret[rid+i][cid] = c;
46992                 }
46993             });
46994         }, this);
46995         return ret;
46996     
46997     },
46998     
46999     normalizeRow: function(row)
47000     {
47001         var ret= [];
47002         row.forEach(function(c) {
47003             if (c.colspan < 2) {
47004                 ret.push(c);
47005                 return;
47006             }
47007             for(var i =0 ;i < c.colspan; i++) {
47008                 ret.push(c);
47009             }
47010         });
47011         return ret;
47012     
47013     },
47014     
47015     deleteColumn : function(sel)
47016     {
47017         if (!sel || sel.type != 'col') {
47018             return;
47019         }
47020         if (this.no_col < 2) {
47021             return;
47022         }
47023         
47024         this.rows.forEach(function(row) {
47025             var cols = this.normalizeRow(row);
47026             var col = cols[sel.col];
47027             if (col.colspan > 1) {
47028                 col.colspan --;
47029             } else {
47030                 row.remove(col);
47031             }
47032             
47033         }, this);
47034         this.no_col--;
47035         
47036     },
47037     removeColumn : function()
47038     {
47039         this.deleteColumn({
47040             type: 'col',
47041             col : this.no_col-1
47042         });
47043         this.updateElement();
47044     },
47045     
47046      
47047     addColumn : function()
47048     {
47049         
47050         this.rows.forEach(function(row) {
47051             row.push(this.emptyCell());
47052            
47053         }, this);
47054         this.updateElement();
47055     },
47056     
47057     deleteRow : function(sel)
47058     {
47059         if (!sel || sel.type != 'row') {
47060             return;
47061         }
47062         
47063         if (this.no_row < 2) {
47064             return;
47065         }
47066         
47067         var rows = this.normalizeRows();
47068         
47069         
47070         rows[sel.row].forEach(function(col) {
47071             if (col.rowspan > 1) {
47072                 col.rowspan--;
47073             } else {
47074                 col.remove = 1; // flage it as removed.
47075             }
47076             
47077         }, this);
47078         var newrows = [];
47079         this.rows.forEach(function(row) {
47080             newrow = [];
47081             row.forEach(function(c) {
47082                 if (typeof(c.remove) == 'undefined') {
47083                     newrow.push(c);
47084                 }
47085                 
47086             });
47087             if (newrow.length > 0) {
47088                 newrows.push(row);
47089             }
47090         });
47091         this.rows =  newrows;
47092         
47093         
47094         
47095         this.no_row--;
47096         this.updateElement();
47097         
47098     },
47099     removeRow : function()
47100     {
47101         this.deleteRow({
47102             type: 'row',
47103             row : this.no_row-1
47104         });
47105         
47106     },
47107     
47108      
47109     addRow : function()
47110     {
47111         
47112         row = [];
47113         for (var i = 0; i < this.no_col; i++ ) {
47114             
47115             row.push(this.emptyCell());
47116            
47117         }
47118         this.rows.push(row);
47119         this.updateElement();
47120         
47121     },
47122      
47123     // the default cell object... at present...
47124     emptyCell : function() {
47125         return (new Roo.htmleditor.BlockTd({})).toObject();
47126         
47127      
47128     },
47129     
47130     removeNode : function()
47131     {
47132         return this.node;
47133     },
47134     
47135     
47136     
47137     resetWidths : function()
47138     {
47139         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
47140             var nn = Roo.htmleditor.Block.factory(n);
47141             nn.width = '';
47142             nn.updateElement(n);
47143         });
47144     }
47145     
47146     
47147     
47148     
47149 })
47150
47151 /**
47152  *
47153  * editing a TD?
47154  *
47155  * since selections really work on the table cell, then editing really should work from there
47156  *
47157  * The original plan was to support merging etc... - but that may not be needed yet..
47158  *
47159  * So this simple version will support:
47160  *   add/remove cols
47161  *   adjust the width +/-
47162  *   reset the width...
47163  *   
47164  *
47165  */
47166
47167
47168  
47169
47170 /**
47171  * @class Roo.htmleditor.BlockTable
47172  * Block that manages a table
47173  * 
47174  * @constructor
47175  * Create a new Filter.
47176  * @param {Object} config Configuration options
47177  */
47178
47179 Roo.htmleditor.BlockTd = function(cfg)
47180 {
47181     if (cfg.node) {
47182         this.readElement(cfg.node);
47183         this.updateElement(cfg.node);
47184     }
47185     Roo.apply(this, cfg);
47186      
47187     
47188     
47189 }
47190 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
47191  
47192     node : false,
47193     
47194     width: '',
47195     textAlign : 'left',
47196     
47197     colspan : 1,
47198     rowspan : 1,
47199     
47200     
47201     // used by context menu
47202     friendly_name : 'Table Cell',
47203     deleteTitle : 'Delete Table',
47204     
47205     // context menu is drawn once..
47206     
47207     contextMenu : function(toolbar)
47208     {
47209         
47210         var cell = function() {
47211             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
47212         };
47213         
47214         var table = function() {
47215             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
47216         };
47217         
47218         var lr = false;
47219         var saveSel = function()
47220         {
47221             lr = toolbar.editorcore.getSelection().getRangeAt(0);
47222         }
47223         var restoreSel = function()
47224         {
47225             if (lr) {
47226                 (function() {
47227                     toolbar.editorcore.focus();
47228                     var cr = toolbar.editorcore.getSelection();
47229                     cr.removeAllRanges();
47230                     cr.addRange(lr);
47231                     toolbar.editorcore.onEditorEvent();
47232                 }).defer(10, this);
47233                 
47234                 
47235             }
47236         }
47237         
47238         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
47239         
47240         var syncValue = toolbar.editorcore.syncValue;
47241         
47242         var fields = {};
47243         
47244         return [
47245             {
47246                 xtype : 'Button',
47247                 text : 'Edit Table',
47248                 listeners : {
47249                     click : function() {
47250                         var t = toolbar.tb.selectedNode.closest('table');
47251                         toolbar.editorcore.selectNode(t);
47252                         toolbar.editorcore.onEditorEvent();                        
47253                     }
47254                 }
47255                 
47256             },
47257               
47258            
47259              
47260             {
47261                 xtype : 'TextItem',
47262                 text : "Column Width: ",
47263                  xns : rooui.Toolbar 
47264                
47265             },
47266             {
47267                 xtype : 'Button',
47268                 text: '-',
47269                 listeners : {
47270                     click : function (_self, e)
47271                     {
47272                         saveSel();
47273                         cell().shrinkColumn();
47274                         syncValue();
47275                         restoreSel();
47276                     }
47277                 },
47278                 xns : rooui.Toolbar
47279             },
47280             {
47281                 xtype : 'Button',
47282                 text: '+',
47283                 listeners : {
47284                     click : function (_self, e)
47285                     {
47286                         saveSel();
47287                         cell().growColumn();
47288                         syncValue();
47289                         restoreSel();
47290                     }
47291                 },
47292                 xns : rooui.Toolbar
47293             },
47294             {
47295                 xtype : 'TextItem',
47296                 text : "Merge Cells: ",
47297                  xns : rooui.Toolbar 
47298                
47299             },
47300             
47301             
47302             {
47303                 xtype : 'Button',
47304                 text: 'Right',
47305                 listeners : {
47306                     click : function (_self, e)
47307                     {
47308                         saveSel();
47309                         cell().mergeRight();
47310                         //block().growColumn();
47311                         syncValue();
47312                         restoreSel();
47313                     }
47314                 },
47315                 xns : rooui.Toolbar
47316             },
47317              
47318             {
47319                 xtype : 'Button',
47320                 text: 'Below',
47321                 listeners : {
47322                     click : function (_self, e)
47323                     {
47324                         saveSel();
47325                         cell().mergeBelow();
47326                         //block().growColumn();
47327                         syncValue();
47328                         restoreSel();
47329                     }
47330                 },
47331                 xns : rooui.Toolbar
47332             },
47333             {
47334                 xtype : 'TextItem',
47335                 text : "| ",
47336                  xns : rooui.Toolbar 
47337                
47338             },
47339             
47340             {
47341                 xtype : 'Button',
47342                 text: 'Split',
47343                 listeners : {
47344                     click : function (_self, e)
47345                     {
47346                         saveSel();
47347                         cell().split();
47348                         syncValue();
47349                         restoreSel();
47350                     }
47351                 },
47352                 xns : rooui.Toolbar
47353             }
47354             // align... << fixme
47355             
47356         ];
47357         
47358     },
47359     
47360     
47361   /**
47362      * create a DomHelper friendly object - for use with
47363      * Roo.DomHelper.markup / overwrite / etc..
47364      * ?? should it be called with option to hide all editing features?
47365      */
47366  /**
47367      * create a DomHelper friendly object - for use with
47368      * Roo.DomHelper.markup / overwrite / etc..
47369      * ?? should it be called with option to hide all editing features?
47370      */
47371     toObject : function()
47372     {
47373         
47374         var ret = {
47375             tag : 'td',
47376             contenteditable : 'true', // this stops cell selection from picking the table.
47377             'data-block' : 'Td',
47378             width:  this.width,
47379             style : {  
47380                 width:  this.width,
47381                 'text-align' :  this.textAlign,
47382                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
47383                 'border-collapse' : 'collapse' 
47384             },
47385             html : this.html
47386         };
47387         
47388         if (this.colspan > 1) {
47389             ret.colspan = cell.colspan ;
47390         } 
47391         if (ret.rowspan > 1) {
47392             this.rowspan = cell.rowspan ;
47393         }
47394         
47395            
47396         
47397         return ret;
47398          
47399     },
47400     
47401     readElement : function(node)
47402     {
47403         node  = node ? node : this.node ;
47404         this.width = node.style.width;
47405         
47406         this.html = node.innerHTML;
47407         
47408         
47409     },
47410      
47411     // the default cell object... at present...
47412     emptyCell : function() {
47413         return {
47414             colspan :  1,
47415             rowspan :  1,
47416             textAlign : 'left',
47417             html : "&nbsp;" // is this going to be editable now?
47418         };
47419      
47420     },
47421     
47422     removeNode : function()
47423     {
47424         return this.node.closest('table');
47425         
47426         
47427     },
47428     
47429     cellData : false,
47430     
47431     colWidths : false,
47432     
47433     toTableArray  : function()
47434     {
47435         var ret = [];
47436         var tab = this.node.closest('tr').closest('table');
47437         Array.from(tab.rows).forEach(function(r, ri){
47438             ret[ri] = [];
47439         });
47440         var rn = 0;
47441         this.colWidths = [];
47442         var all_auto = true;
47443         Array.from(tab.rows).forEach(function(r, ri){
47444             
47445             var cn = 0;
47446             Array.from(r.cells).forEach(function(ce, ci){
47447                 var c =  {
47448                     cell : ce,
47449                     row : rn,
47450                     col: cn,
47451                     colspan : ce.colSpan,
47452                     rowspan : ce.rowSpan
47453                 };
47454                 if (ce.isEqualNode(this.node)) {
47455                     this.cellData = c;
47456                 }
47457                 // if we have been filled up by a row?
47458                 if (typeof(ret[rn][cn]) != 'undefined') {
47459                     while(typeof(ret[rn][cn]) != 'undefined') {
47460                         cn++;
47461                     }
47462                     c.col = cn;
47463                 }
47464                 
47465                 if (typeof(this.colWidths[cn]) == 'undefined') {
47466                     this.colWidths[cn] =   ce.style.width;
47467                     if (this.colWidths[cn] != '') {
47468                         all_auto = false;
47469                     }
47470                 }
47471                 
47472                 
47473                 if (c.colspan < 2 && c.rowspan < 2 ) {
47474                     ret[rn][cn] = c;
47475                     cn++;
47476                     return;
47477                 }
47478                 for(var j = 0; j < c.rowspan; j++) {
47479                     if (typeof(ret[rn+j]) == 'undefined') {
47480                         continue; // we have a problem..
47481                     }
47482                     ret[rn+j][cn] = c;
47483                     for(var i = 0; i < c.colspan; i++) {
47484                         ret[rn+j][cn+i] = c;
47485                     }
47486                 }
47487                 
47488                 cn += c.colspan;
47489             }, this);
47490             rn++;
47491         }, this);
47492         
47493         // initalize widths.?
47494         // either all widths or no widths..
47495         if (all_auto) {
47496             this.colWidths[0] = false; // no widths flag.
47497         }
47498         
47499         
47500         return ret;
47501         
47502     },
47503     
47504     
47505     
47506     
47507     mergeRight: function()
47508     {
47509          
47510         // get the contents of the next cell along..
47511         var tr = this.node.closest('tr');
47512         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
47513         if (i >= tr.childNodes.length - 1) {
47514             return; // no cells on right to merge with.
47515         }
47516         var table = this.toTableArray();
47517         
47518         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
47519             return; // nothing right?
47520         }
47521         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
47522         // right cell - must be same rowspan and on the same row.
47523         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
47524             return; // right hand side is not same rowspan.
47525         }
47526         
47527         
47528         
47529         this.node.innerHTML += ' ' + rc.cell.innerHTML;
47530         tr.removeChild(rc.cell);
47531         this.colspan += rc.colspan;
47532         this.node.setAttribute('colspan', this.colspan);
47533
47534     },
47535     
47536     
47537     mergeBelow : function()
47538     {
47539         var table = this.toTableArray();
47540         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
47541             return; // no row below
47542         }
47543         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
47544             return; // nothing right?
47545         }
47546         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
47547         
47548         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
47549             return; // right hand side is not same rowspan.
47550         }
47551         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
47552         rc.cell.parentNode.removeChild(rc.cell);
47553         this.rowspan += rc.rowspan;
47554         this.node.setAttribute('rowspan', this.rowspan);
47555     },
47556     
47557     split: function()
47558     {
47559         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
47560             return;
47561         }
47562         var table = this.toTableArray();
47563         var cd = this.cellData;
47564         this.rowspan = 1;
47565         this.colspan = 1;
47566         
47567         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
47568             
47569             
47570             
47571             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
47572                 if (r == cd.row && c == cd.col) {
47573                     this.node.removeAttribute('rowspan');
47574                     this.node.removeAttribute('colspan');
47575                     continue;
47576                 }
47577                  
47578                 var ntd = this.node.cloneNode(); // which col/row should be 0..
47579                 ntd.removeAttribute('id'); //
47580                 //ntd.style.width  = '';
47581                 ntd.innerHTML = '';
47582                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
47583             }
47584             
47585         }
47586         this.redrawAllCells(table);
47587         
47588          
47589         
47590     },
47591     
47592     
47593     
47594     redrawAllCells: function(table)
47595     {
47596         
47597          
47598         var tab = this.node.closest('tr').closest('table');
47599         Array.from(tab.rows).forEach(function(r, ri){
47600             Array.from(r.cells).forEach(function(ce, ci){
47601                 ce.parentNode.removeChild(ce);
47602             });
47603         });
47604         for(var r = 0 ; r < table.length; r++) {
47605             var re = tab.rows[r];
47606             for(var c = 0 ; c < table[r].length; c++) {
47607                 if (table[r][c].cell === false) {
47608                     continue;
47609                 }
47610                 
47611                 re.appendChild(table[r][c].cell);
47612                  
47613                 table[r][c].cell = false;
47614             }
47615         }
47616         
47617     },
47618     updateWidths : function(table)
47619     {
47620         for(var r = 0 ; r < table.length; r++) {
47621            
47622             for(var c = 0 ; c < table[r].length; c++) {
47623                 if (table[r][c].cell === false) {
47624                     continue;
47625                 }
47626                 
47627                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
47628                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
47629                     el.width = Math.floor(this.colWidths[c])  +'%';
47630                     el.updateElement(el.node);
47631                 }
47632                 table[r][c].cell = false; // done
47633             }
47634         }
47635     },
47636     normalizeWidths : function(table)
47637     {
47638     
47639         if (this.colWidths[0] === false) {
47640             var nw = 100.0 / this.colWidths.length;
47641             this.colWidths.forEach(function(w,i) {
47642                 this.colWidths[i] = nw;
47643             },this);
47644             return;
47645         }
47646     
47647         var t = 0, missing = [];
47648         
47649         this.colWidths.forEach(function(w,i) {
47650             //if you mix % and
47651             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
47652             var add =  this.colWidths[i];
47653             if (add > 0) {
47654                 t+=add;
47655                 return;
47656             }
47657             missing.push(i);
47658             
47659             
47660         },this);
47661         var nc = this.colWidths.length;
47662         if (missing.length) {
47663             var mult = (nc - missing.length) / (1.0 * nc);
47664             var t = mult * t;
47665             var ew = (100 -t) / (1.0 * missing.length);
47666             this.colWidths.forEach(function(w,i) {
47667                 if (w > 0) {
47668                     this.colWidths[i] = w * mult;
47669                     return;
47670                 }
47671                 
47672                 this.colWidths[i] = ew;
47673             }, this);
47674             // have to make up numbers..
47675              
47676         }
47677         // now we should have all the widths..
47678         
47679     
47680     },
47681     
47682     shrinkColumn : function()
47683     {
47684         var table = this.toTableArray();
47685         this.normalizeWidths(table);
47686         var col = this.cellData.col;
47687         var nw = this.colWidths[col] * 0.8;
47688         if (nw < 5) {
47689             return;
47690         }
47691         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
47692         this.colWidths.forEach(function(w,i) {
47693             if (i == col) {
47694                  this.colWidths[i] = nw;
47695                 return;
47696             }
47697             this.colWidths[i] += otherAdd
47698         }, this);
47699         this.updateWidths(table);
47700          
47701     },
47702     growColumn : function()
47703     {
47704         var table = this.toTableArray();
47705         this.normalizeWidths(table);
47706         var col = this.cellData.col;
47707         var nw = this.colWidths[col] * 1.2;
47708         if (nw > 90) {
47709             return;
47710         }
47711         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
47712         this.colWidths.forEach(function(w,i) {
47713             if (i == col) {
47714                 this.colWidths[i] = nw;
47715                 return;
47716             }
47717             this.colWidths[i] -= otherSub
47718         }, this);
47719         this.updateWidths(table);
47720          
47721     }
47722     
47723     
47724 })
47725
47726 //<script type="text/javascript">
47727
47728 /*
47729  * Based  Ext JS Library 1.1.1
47730  * Copyright(c) 2006-2007, Ext JS, LLC.
47731  * LGPL
47732  *
47733  */
47734  
47735 /**
47736  * @class Roo.HtmlEditorCore
47737  * @extends Roo.Component
47738  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
47739  *
47740  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
47741  */
47742
47743 Roo.HtmlEditorCore = function(config){
47744     
47745     
47746     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
47747     
47748     
47749     this.addEvents({
47750         /**
47751          * @event initialize
47752          * Fires when the editor is fully initialized (including the iframe)
47753          * @param {Roo.HtmlEditorCore} this
47754          */
47755         initialize: true,
47756         /**
47757          * @event activate
47758          * Fires when the editor is first receives the focus. Any insertion must wait
47759          * until after this event.
47760          * @param {Roo.HtmlEditorCore} this
47761          */
47762         activate: true,
47763          /**
47764          * @event beforesync
47765          * Fires before the textarea is updated with content from the editor iframe. Return false
47766          * to cancel the sync.
47767          * @param {Roo.HtmlEditorCore} this
47768          * @param {String} html
47769          */
47770         beforesync: true,
47771          /**
47772          * @event beforepush
47773          * Fires before the iframe editor is updated with content from the textarea. Return false
47774          * to cancel the push.
47775          * @param {Roo.HtmlEditorCore} this
47776          * @param {String} html
47777          */
47778         beforepush: true,
47779          /**
47780          * @event sync
47781          * Fires when the textarea is updated with content from the editor iframe.
47782          * @param {Roo.HtmlEditorCore} this
47783          * @param {String} html
47784          */
47785         sync: true,
47786          /**
47787          * @event push
47788          * Fires when the iframe editor is updated with content from the textarea.
47789          * @param {Roo.HtmlEditorCore} this
47790          * @param {String} html
47791          */
47792         push: true,
47793         
47794         /**
47795          * @event editorevent
47796          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
47797          * @param {Roo.HtmlEditorCore} this
47798          */
47799         editorevent: true
47800         
47801     });
47802     
47803     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
47804     
47805     // defaults : white / black...
47806     this.applyBlacklists();
47807     
47808     
47809     
47810 };
47811
47812
47813 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
47814
47815
47816      /**
47817      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
47818      */
47819     
47820     owner : false,
47821     
47822      /**
47823      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
47824      *                        Roo.resizable.
47825      */
47826     resizable : false,
47827      /**
47828      * @cfg {Number} height (in pixels)
47829      */   
47830     height: 300,
47831    /**
47832      * @cfg {Number} width (in pixels)
47833      */   
47834     width: 500,
47835     
47836     /**
47837      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
47838      * 
47839      */
47840     stylesheets: false,
47841     
47842     /**
47843      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
47844      */
47845     allowComments: false,
47846     // id of frame..
47847     frameId: false,
47848     
47849     // private properties
47850     validationEvent : false,
47851     deferHeight: true,
47852     initialized : false,
47853     activated : false,
47854     sourceEditMode : false,
47855     onFocus : Roo.emptyFn,
47856     iframePad:3,
47857     hideMode:'offsets',
47858     
47859     clearUp: true,
47860     
47861     // blacklist + whitelisted elements..
47862     black: false,
47863     white: false,
47864      
47865     bodyCls : '',
47866
47867     
47868     undoManager : false,
47869     /**
47870      * Protected method that will not generally be called directly. It
47871      * is called when the editor initializes the iframe with HTML contents. Override this method if you
47872      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
47873      */
47874     getDocMarkup : function(){
47875         // body styles..
47876         var st = '';
47877         
47878         // inherit styels from page...?? 
47879         if (this.stylesheets === false) {
47880             
47881             Roo.get(document.head).select('style').each(function(node) {
47882                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
47883             });
47884             
47885             Roo.get(document.head).select('link').each(function(node) { 
47886                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
47887             });
47888             
47889         } else if (!this.stylesheets.length) {
47890                 // simple..
47891                 st = '<style type="text/css">' +
47892                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
47893                    '</style>';
47894         } else {
47895             for (var i in this.stylesheets) {
47896                 if (typeof(this.stylesheets[i]) != 'string') {
47897                     continue;
47898                 }
47899                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
47900             }
47901             
47902         }
47903         
47904         st +=  '<style type="text/css">' +
47905             'IMG { cursor: pointer } ' +
47906         '</style>';
47907
47908         var cls = 'roo-htmleditor-body';
47909         
47910         if(this.bodyCls.length){
47911             cls += ' ' + this.bodyCls;
47912         }
47913         
47914         return '<html><head>' + st  +
47915             //<style type="text/css">' +
47916             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
47917             //'</style>' +
47918             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
47919     },
47920
47921     // private
47922     onRender : function(ct, position)
47923     {
47924         var _t = this;
47925         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
47926         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
47927         
47928         
47929         this.el.dom.style.border = '0 none';
47930         this.el.dom.setAttribute('tabIndex', -1);
47931         this.el.addClass('x-hidden hide');
47932         
47933         
47934         
47935         if(Roo.isIE){ // fix IE 1px bogus margin
47936             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
47937         }
47938        
47939         
47940         this.frameId = Roo.id();
47941         
47942          
47943         
47944         var iframe = this.owner.wrap.createChild({
47945             tag: 'iframe',
47946             cls: 'form-control', // bootstrap..
47947             id: this.frameId,
47948             name: this.frameId,
47949             frameBorder : 'no',
47950             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
47951         }, this.el
47952         );
47953         
47954         
47955         this.iframe = iframe.dom;
47956
47957         this.assignDocWin();
47958         
47959         this.doc.designMode = 'on';
47960        
47961         this.doc.open();
47962         this.doc.write(this.getDocMarkup());
47963         this.doc.close();
47964
47965         
47966         var task = { // must defer to wait for browser to be ready
47967             run : function(){
47968                 //console.log("run task?" + this.doc.readyState);
47969                 this.assignDocWin();
47970                 if(this.doc.body || this.doc.readyState == 'complete'){
47971                     try {
47972                         this.doc.designMode="on";
47973                         
47974                     } catch (e) {
47975                         return;
47976                     }
47977                     Roo.TaskMgr.stop(task);
47978                     this.initEditor.defer(10, this);
47979                 }
47980             },
47981             interval : 10,
47982             duration: 10000,
47983             scope: this
47984         };
47985         Roo.TaskMgr.start(task);
47986
47987     },
47988
47989     // private
47990     onResize : function(w, h)
47991     {
47992          Roo.log('resize: ' +w + ',' + h );
47993         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
47994         if(!this.iframe){
47995             return;
47996         }
47997         if(typeof w == 'number'){
47998             
47999             this.iframe.style.width = w + 'px';
48000         }
48001         if(typeof h == 'number'){
48002             
48003             this.iframe.style.height = h + 'px';
48004             if(this.doc){
48005                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
48006             }
48007         }
48008         
48009     },
48010
48011     /**
48012      * Toggles the editor between standard and source edit mode.
48013      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
48014      */
48015     toggleSourceEdit : function(sourceEditMode){
48016         
48017         this.sourceEditMode = sourceEditMode === true;
48018         
48019         if(this.sourceEditMode){
48020  
48021             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
48022             
48023         }else{
48024             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
48025             //this.iframe.className = '';
48026             this.deferFocus();
48027         }
48028         //this.setSize(this.owner.wrap.getSize());
48029         //this.fireEvent('editmodechange', this, this.sourceEditMode);
48030     },
48031
48032     
48033   
48034
48035     /**
48036      * Protected method that will not generally be called directly. If you need/want
48037      * custom HTML cleanup, this is the method you should override.
48038      * @param {String} html The HTML to be cleaned
48039      * return {String} The cleaned HTML
48040      */
48041     cleanHtml : function(html){
48042         html = String(html);
48043         if(html.length > 5){
48044             if(Roo.isSafari){ // strip safari nonsense
48045                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
48046             }
48047         }
48048         if(html == '&nbsp;'){
48049             html = '';
48050         }
48051         return html;
48052     },
48053
48054     /**
48055      * HTML Editor -> Textarea
48056      * Protected method that will not generally be called directly. Syncs the contents
48057      * of the editor iframe with the textarea.
48058      */
48059     syncValue : function()
48060     {
48061         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
48062         if(this.initialized){
48063             
48064             this.undoManager.addEvent();
48065
48066             
48067             var bd = (this.doc.body || this.doc.documentElement);
48068             //this.cleanUpPaste(); -- this is done else where and causes havoc..
48069             
48070             // not sure if this is really the place for this
48071             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
48072             // this has to update attributes that get duped.. like alt and caption..
48073             
48074             
48075             //Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
48076             //     Roo.htmleditor.Block.factory(e);
48077             //},this);
48078             
48079             
48080             var div = document.createElement('div');
48081             div.innerHTML = bd.innerHTML;
48082             // remove content editable. (blocks)
48083             
48084            
48085             
48086             //?? tidy?
48087             new Roo.htmleditor.FilterBlock({ node : div });
48088             
48089             var html = div.innerHTML;
48090             if(Roo.isSafari){
48091                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
48092                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
48093                 if(m && m[1]){
48094                     html = '<div style="'+m[0]+'">' + html + '</div>';
48095                 }
48096             }
48097             html = this.cleanHtml(html);
48098             // fix up the special chars.. normaly like back quotes in word...
48099             // however we do not want to do this with chinese..
48100             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
48101                 
48102                 var cc = match.charCodeAt();
48103
48104                 // Get the character value, handling surrogate pairs
48105                 if (match.length == 2) {
48106                     // It's a surrogate pair, calculate the Unicode code point
48107                     var high = match.charCodeAt(0) - 0xD800;
48108                     var low  = match.charCodeAt(1) - 0xDC00;
48109                     cc = (high * 0x400) + low + 0x10000;
48110                 }  else if (
48111                     (cc >= 0x4E00 && cc < 0xA000 ) ||
48112                     (cc >= 0x3400 && cc < 0x4E00 ) ||
48113                     (cc >= 0xf900 && cc < 0xfb00 )
48114                 ) {
48115                         return match;
48116                 }  
48117          
48118                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
48119                 return "&#" + cc + ";";
48120                 
48121                 
48122             });
48123             
48124             
48125              
48126             if(this.owner.fireEvent('beforesync', this, html) !== false){
48127                 this.el.dom.value = html;
48128                 this.owner.fireEvent('sync', this, html);
48129             }
48130         }
48131     },
48132
48133     /**
48134      * TEXTAREA -> EDITABLE
48135      * Protected method that will not generally be called directly. Pushes the value of the textarea
48136      * into the iframe editor.
48137      */
48138     pushValue : function()
48139     {
48140         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
48141         if(this.initialized){
48142             var v = this.el.dom.value.trim();
48143             
48144             
48145             if(this.owner.fireEvent('beforepush', this, v) !== false){
48146                 var d = (this.doc.body || this.doc.documentElement);
48147                 d.innerHTML = v;
48148                  
48149                 this.el.dom.value = d.innerHTML;
48150                 this.owner.fireEvent('push', this, v);
48151             }
48152             Roo.htmleditor.Block.initAll(this.doc.body);
48153             
48154             var lc = this.doc.body.lastChild;
48155             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
48156                 // add an extra line at the end.
48157                 this.doc.body.appendChild(this.doc.createElement('br'));
48158             }
48159             
48160             
48161         }
48162     },
48163
48164     // private
48165     deferFocus : function(){
48166         this.focus.defer(10, this);
48167     },
48168
48169     // doc'ed in Field
48170     focus : function(){
48171         if(this.win && !this.sourceEditMode){
48172             this.win.focus();
48173         }else{
48174             this.el.focus();
48175         }
48176     },
48177     
48178     assignDocWin: function()
48179     {
48180         var iframe = this.iframe;
48181         
48182          if(Roo.isIE){
48183             this.doc = iframe.contentWindow.document;
48184             this.win = iframe.contentWindow;
48185         } else {
48186 //            if (!Roo.get(this.frameId)) {
48187 //                return;
48188 //            }
48189 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
48190 //            this.win = Roo.get(this.frameId).dom.contentWindow;
48191             
48192             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
48193                 return;
48194             }
48195             
48196             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
48197             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
48198         }
48199     },
48200     
48201     // private
48202     initEditor : function(){
48203         //console.log("INIT EDITOR");
48204         this.assignDocWin();
48205         
48206         
48207         
48208         this.doc.designMode="on";
48209         this.doc.open();
48210         this.doc.write(this.getDocMarkup());
48211         this.doc.close();
48212         
48213         var dbody = (this.doc.body || this.doc.documentElement);
48214         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
48215         // this copies styles from the containing element into thsi one..
48216         // not sure why we need all of this..
48217         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
48218         
48219         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
48220         //ss['background-attachment'] = 'fixed'; // w3c
48221         dbody.bgProperties = 'fixed'; // ie
48222         //Roo.DomHelper.applyStyles(dbody, ss);
48223         Roo.EventManager.on(this.doc, {
48224             //'mousedown': this.onEditorEvent,
48225             'mouseup': this.onEditorEvent,
48226             'dblclick': this.onEditorEvent,
48227             'click': this.onEditorEvent,
48228             'keyup': this.onEditorEvent,
48229             
48230             buffer:100,
48231             scope: this
48232         });
48233         Roo.EventManager.on(this.doc, {
48234             'paste': this.onPasteEvent,
48235             scope : this
48236         });
48237         if(Roo.isGecko){
48238             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
48239         }
48240         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
48241             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
48242         }
48243         this.initialized = true;
48244
48245         
48246         // initialize special key events - enter
48247         new Roo.htmleditor.KeyEnter({core : this});
48248         
48249          
48250         
48251         this.owner.fireEvent('initialize', this);
48252         this.pushValue();
48253     },
48254     
48255     onPasteEvent : function(e,v)
48256     {
48257         // I think we better assume paste is going to be a dirty load of rubish from word..
48258         
48259         // even pasting into a 'email version' of this widget will have to clean up that mess.
48260         var cd = (e.browserEvent.clipboardData || window.clipboardData);
48261         
48262         // check what type of paste - if it's an image, then handle it differently.
48263         if (cd.files.length > 0) {
48264             // pasting images?
48265             var urlAPI = (window.createObjectURL && window) || 
48266                 (window.URL && URL.revokeObjectURL && URL) || 
48267                 (window.webkitURL && webkitURL);
48268     
48269             var url = urlAPI.createObjectURL( cd.files[0]);
48270             this.insertAtCursor('<img src=" + url + ">');
48271             return false;
48272         }
48273         
48274         var html = cd.getData('text/html'); // clipboard event
48275         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
48276         var images = parser.doc ? parser.doc.getElementsByType('pict') : [];
48277         Roo.log(images);
48278         //Roo.log(imgs);
48279         // fixme..
48280         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
48281                        .map(function(g) { return g.toDataURL(); });
48282         
48283         
48284         html = this.cleanWordChars(html);
48285         
48286         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
48287         
48288         
48289         var sn = this.getParentElement();
48290         // check if d contains a table, and prevent nesting??
48291         //Roo.log(d.getElementsByTagName('table'));
48292         //Roo.log(sn);
48293         //Roo.log(sn.closest('table'));
48294         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
48295             e.preventDefault();
48296             this.insertAtCursor("You can not nest tables");
48297             //Roo.log("prevent?"); // fixme - 
48298             return false;
48299         }
48300         
48301         if (images.length > 0) {
48302             Roo.each(d.getElementsByTagName('img'), function(img, i) {
48303                 img.setAttribute('src', images[i]);
48304             });
48305         }
48306         
48307       
48308         new Roo.htmleditor.FilterStyleToTag({ node : d });
48309         new Roo.htmleditor.FilterAttributes({
48310             node : d,
48311             attrib_white : ['href', 'src', 'name', 'align'],
48312             attrib_clean : ['href', 'src' ] 
48313         });
48314         new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
48315         // should be fonts..
48316         new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
48317         new Roo.htmleditor.FilterParagraph({ node : d });
48318         new Roo.htmleditor.FilterSpan({ node : d });
48319         new Roo.htmleditor.FilterLongBr({ node : d });
48320         
48321         
48322         
48323         this.insertAtCursor(d.innerHTML);
48324         Roo.htmleditor.Block.initAll(this.doc.body);
48325         
48326         
48327         e.preventDefault();
48328         return false;
48329         // default behaveiour should be our local cleanup paste? (optional?)
48330         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
48331         //this.owner.fireEvent('paste', e, v);
48332     },
48333     // private
48334     onDestroy : function(){
48335         
48336         
48337         
48338         if(this.rendered){
48339             
48340             //for (var i =0; i < this.toolbars.length;i++) {
48341             //    // fixme - ask toolbars for heights?
48342             //    this.toolbars[i].onDestroy();
48343            // }
48344             
48345             //this.wrap.dom.innerHTML = '';
48346             //this.wrap.remove();
48347         }
48348     },
48349
48350     // private
48351     onFirstFocus : function(){
48352         
48353         this.assignDocWin();
48354         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
48355         
48356         this.activated = true;
48357          
48358     
48359         if(Roo.isGecko){ // prevent silly gecko errors
48360             this.win.focus();
48361             var s = this.win.getSelection();
48362             if(!s.focusNode || s.focusNode.nodeType != 3){
48363                 var r = s.getRangeAt(0);
48364                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
48365                 r.collapse(true);
48366                 this.deferFocus();
48367             }
48368             try{
48369                 this.execCmd('useCSS', true);
48370                 this.execCmd('styleWithCSS', false);
48371             }catch(e){}
48372         }
48373         this.owner.fireEvent('activate', this);
48374     },
48375
48376     // private
48377     adjustFont: function(btn){
48378         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
48379         //if(Roo.isSafari){ // safari
48380         //    adjust *= 2;
48381        // }
48382         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
48383         if(Roo.isSafari){ // safari
48384             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
48385             v =  (v < 10) ? 10 : v;
48386             v =  (v > 48) ? 48 : v;
48387             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
48388             
48389         }
48390         
48391         
48392         v = Math.max(1, v+adjust);
48393         
48394         this.execCmd('FontSize', v  );
48395     },
48396
48397     onEditorEvent : function(e)
48398     {
48399         
48400         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
48401             return; // we do not handle this.. (undo manager does..)
48402         }
48403         // in theory this detects if the last element is not a br, then we try and do that.
48404         // its so clicking in space at bottom triggers adding a br and moving the cursor.
48405         if (e &&
48406             e.target.nodeName == 'BODY' &&
48407             e.type == "mouseup" &&
48408             this.doc.body.lastChild
48409            ) {
48410             var lc = this.doc.body.lastChild;
48411             while (lc.nodeType == 3 && lc.nodeValue == '') {
48412                 lc = lc.previousSibling;
48413             }
48414             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
48415             // if last element is <BR> - then dont do anything.
48416             
48417                 var ns = this.doc.createElement('br');
48418                 this.doc.body.appendChild(ns);
48419                 range = this.doc.createRange();
48420                 range.setStartAfter(ns);
48421                 range.collapse(true);
48422                 var sel = this.win.getSelection();
48423                 sel.removeAllRanges();
48424                 sel.addRange(range);
48425             }
48426         }
48427         
48428         
48429         
48430         this.owner.fireEvent('editorevent', this, e);
48431       //  this.updateToolbar();
48432         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
48433     },
48434
48435     insertTag : function(tg)
48436     {
48437         // could be a bit smarter... -> wrap the current selected tRoo..
48438         if (tg.toLowerCase() == 'span' ||
48439             tg.toLowerCase() == 'code' ||
48440             tg.toLowerCase() == 'sup' ||
48441             tg.toLowerCase() == 'sub' 
48442             ) {
48443             
48444             range = this.createRange(this.getSelection());
48445             var wrappingNode = this.doc.createElement(tg.toLowerCase());
48446             wrappingNode.appendChild(range.extractContents());
48447             range.insertNode(wrappingNode);
48448
48449             return;
48450             
48451             
48452             
48453         }
48454         this.execCmd("formatblock",   tg);
48455         this.undoManager.addEvent(); 
48456     },
48457     
48458     insertText : function(txt)
48459     {
48460         
48461         
48462         var range = this.createRange();
48463         range.deleteContents();
48464                //alert(Sender.getAttribute('label'));
48465                
48466         range.insertNode(this.doc.createTextNode(txt));
48467         this.undoManager.addEvent();
48468     } ,
48469     
48470      
48471
48472     /**
48473      * Executes a Midas editor command on the editor document and performs necessary focus and
48474      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
48475      * @param {String} cmd The Midas command
48476      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
48477      */
48478     relayCmd : function(cmd, value){
48479         this.win.focus();
48480         this.execCmd(cmd, value);
48481         this.owner.fireEvent('editorevent', this);
48482         //this.updateToolbar();
48483         this.owner.deferFocus();
48484     },
48485
48486     /**
48487      * Executes a Midas editor command directly on the editor document.
48488      * For visual commands, you should use {@link #relayCmd} instead.
48489      * <b>This should only be called after the editor is initialized.</b>
48490      * @param {String} cmd The Midas command
48491      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
48492      */
48493     execCmd : function(cmd, value){
48494         this.doc.execCommand(cmd, false, value === undefined ? null : value);
48495         this.syncValue();
48496     },
48497  
48498  
48499    
48500     /**
48501      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
48502      * to insert tRoo.
48503      * @param {String} text | dom node.. 
48504      */
48505     insertAtCursor : function(text)
48506     {
48507         
48508         if(!this.activated){
48509             return;
48510         }
48511          
48512         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
48513             this.win.focus();
48514             
48515             
48516             // from jquery ui (MIT licenced)
48517             var range, node;
48518             var win = this.win;
48519             
48520             if (win.getSelection && win.getSelection().getRangeAt) {
48521                 
48522                 // delete the existing?
48523                 
48524                 this.createRange(this.getSelection()).deleteContents();
48525                 range = win.getSelection().getRangeAt(0);
48526                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
48527                 range.insertNode(node);
48528                 range = range.cloneRange();
48529                 range.collapse(false);
48530                  
48531                 win.getSelection().removeAllRanges();
48532                 win.getSelection().addRange(range);
48533                 
48534                 
48535                 
48536             } else if (win.document.selection && win.document.selection.createRange) {
48537                 // no firefox support
48538                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
48539                 win.document.selection.createRange().pasteHTML(txt);
48540             
48541             } else {
48542                 // no firefox support
48543                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
48544                 this.execCmd('InsertHTML', txt);
48545             } 
48546             this.syncValue();
48547             
48548             this.deferFocus();
48549         }
48550     },
48551  // private
48552     mozKeyPress : function(e){
48553         if(e.ctrlKey){
48554             var c = e.getCharCode(), cmd;
48555           
48556             if(c > 0){
48557                 c = String.fromCharCode(c).toLowerCase();
48558                 switch(c){
48559                     case 'b':
48560                         cmd = 'bold';
48561                         break;
48562                     case 'i':
48563                         cmd = 'italic';
48564                         break;
48565                     
48566                     case 'u':
48567                         cmd = 'underline';
48568                         break;
48569                     
48570                     //case 'v':
48571                       //  this.cleanUpPaste.defer(100, this);
48572                       //  return;
48573                         
48574                 }
48575                 if(cmd){
48576                     this.win.focus();
48577                     this.execCmd(cmd);
48578                     this.deferFocus();
48579                     e.preventDefault();
48580                 }
48581                 
48582             }
48583         }
48584     },
48585
48586     // private
48587     fixKeys : function(){ // load time branching for fastest keydown performance
48588         if(Roo.isIE){
48589             return function(e){
48590                 var k = e.getKey(), r;
48591                 if(k == e.TAB){
48592                     e.stopEvent();
48593                     r = this.doc.selection.createRange();
48594                     if(r){
48595                         r.collapse(true);
48596                         r.pasteHTML('&#160;&#160;&#160;&#160;');
48597                         this.deferFocus();
48598                     }
48599                     return;
48600                 }
48601                 /// this is handled by Roo.htmleditor.KeyEnter
48602                  /*
48603                 if(k == e.ENTER){
48604                     r = this.doc.selection.createRange();
48605                     if(r){
48606                         var target = r.parentElement();
48607                         if(!target || target.tagName.toLowerCase() != 'li'){
48608                             e.stopEvent();
48609                             r.pasteHTML('<br/>');
48610                             r.collapse(false);
48611                             r.select();
48612                         }
48613                     }
48614                 }
48615                 */
48616                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
48617                 //    this.cleanUpPaste.defer(100, this);
48618                 //    return;
48619                 //}
48620                 
48621                 
48622             };
48623         }else if(Roo.isOpera){
48624             return function(e){
48625                 var k = e.getKey();
48626                 if(k == e.TAB){
48627                     e.stopEvent();
48628                     this.win.focus();
48629                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
48630                     this.deferFocus();
48631                 }
48632                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
48633                 //    this.cleanUpPaste.defer(100, this);
48634                  //   return;
48635                 //}
48636                 
48637             };
48638         }else if(Roo.isSafari){
48639             return function(e){
48640                 var k = e.getKey();
48641                 
48642                 if(k == e.TAB){
48643                     e.stopEvent();
48644                     this.execCmd('InsertText','\t');
48645                     this.deferFocus();
48646                     return;
48647                 }
48648                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
48649                  //   this.cleanUpPaste.defer(100, this);
48650                  //   return;
48651                // }
48652                 
48653              };
48654         }
48655     }(),
48656     
48657     getAllAncestors: function()
48658     {
48659         var p = this.getSelectedNode();
48660         var a = [];
48661         if (!p) {
48662             a.push(p); // push blank onto stack..
48663             p = this.getParentElement();
48664         }
48665         
48666         
48667         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
48668             a.push(p);
48669             p = p.parentNode;
48670         }
48671         a.push(this.doc.body);
48672         return a;
48673     },
48674     lastSel : false,
48675     lastSelNode : false,
48676     
48677     
48678     getSelection : function() 
48679     {
48680         this.assignDocWin();
48681         return Roo.isIE ? this.doc.selection : this.win.getSelection();
48682     },
48683     /**
48684      * Select a dom node
48685      * @param {DomElement} node the node to select
48686      */
48687     selectNode : function(node)
48688     {
48689         var nodeRange = node.ownerDocument.createRange();
48690         try {
48691             nodeRange.selectNode(node);
48692         } catch (e) {
48693             nodeRange.selectNodeContents(node);
48694         }
48695         //nodeRange.collapse(true);
48696         var s = this.win.getSelection();
48697         s.removeAllRanges();
48698         s.addRange(nodeRange);
48699     },
48700     
48701     getSelectedNode: function() 
48702     {
48703         // this may only work on Gecko!!!
48704         
48705         // should we cache this!!!!
48706         
48707         
48708         
48709          
48710         var range = this.createRange(this.getSelection()).cloneRange();
48711         
48712         if (Roo.isIE) {
48713             var parent = range.parentElement();
48714             while (true) {
48715                 var testRange = range.duplicate();
48716                 testRange.moveToElementText(parent);
48717                 if (testRange.inRange(range)) {
48718                     break;
48719                 }
48720                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
48721                     break;
48722                 }
48723                 parent = parent.parentElement;
48724             }
48725             return parent;
48726         }
48727         
48728         // is ancestor a text element.
48729         var ac =  range.commonAncestorContainer;
48730         if (ac.nodeType == 3) {
48731             ac = ac.parentNode;
48732         }
48733         
48734         var ar = ac.childNodes;
48735          
48736         var nodes = [];
48737         var other_nodes = [];
48738         var has_other_nodes = false;
48739         for (var i=0;i<ar.length;i++) {
48740             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
48741                 continue;
48742             }
48743             // fullly contained node.
48744             
48745             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
48746                 nodes.push(ar[i]);
48747                 continue;
48748             }
48749             
48750             // probably selected..
48751             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
48752                 other_nodes.push(ar[i]);
48753                 continue;
48754             }
48755             // outer..
48756             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
48757                 continue;
48758             }
48759             
48760             
48761             has_other_nodes = true;
48762         }
48763         if (!nodes.length && other_nodes.length) {
48764             nodes= other_nodes;
48765         }
48766         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
48767             return false;
48768         }
48769         
48770         return nodes[0];
48771     },
48772     createRange: function(sel)
48773     {
48774         // this has strange effects when using with 
48775         // top toolbar - not sure if it's a great idea.
48776         //this.editor.contentWindow.focus();
48777         if (typeof sel != "undefined") {
48778             try {
48779                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
48780             } catch(e) {
48781                 return this.doc.createRange();
48782             }
48783         } else {
48784             return this.doc.createRange();
48785         }
48786     },
48787     getParentElement: function()
48788     {
48789         
48790         this.assignDocWin();
48791         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
48792         
48793         var range = this.createRange(sel);
48794          
48795         try {
48796             var p = range.commonAncestorContainer;
48797             while (p.nodeType == 3) { // text node
48798                 p = p.parentNode;
48799             }
48800             return p;
48801         } catch (e) {
48802             return null;
48803         }
48804     
48805     },
48806     /***
48807      *
48808      * Range intersection.. the hard stuff...
48809      *  '-1' = before
48810      *  '0' = hits..
48811      *  '1' = after.
48812      *         [ -- selected range --- ]
48813      *   [fail]                        [fail]
48814      *
48815      *    basically..
48816      *      if end is before start or  hits it. fail.
48817      *      if start is after end or hits it fail.
48818      *
48819      *   if either hits (but other is outside. - then it's not 
48820      *   
48821      *    
48822      **/
48823     
48824     
48825     // @see http://www.thismuchiknow.co.uk/?p=64.
48826     rangeIntersectsNode : function(range, node)
48827     {
48828         var nodeRange = node.ownerDocument.createRange();
48829         try {
48830             nodeRange.selectNode(node);
48831         } catch (e) {
48832             nodeRange.selectNodeContents(node);
48833         }
48834     
48835         var rangeStartRange = range.cloneRange();
48836         rangeStartRange.collapse(true);
48837     
48838         var rangeEndRange = range.cloneRange();
48839         rangeEndRange.collapse(false);
48840     
48841         var nodeStartRange = nodeRange.cloneRange();
48842         nodeStartRange.collapse(true);
48843     
48844         var nodeEndRange = nodeRange.cloneRange();
48845         nodeEndRange.collapse(false);
48846     
48847         return rangeStartRange.compareBoundaryPoints(
48848                  Range.START_TO_START, nodeEndRange) == -1 &&
48849                rangeEndRange.compareBoundaryPoints(
48850                  Range.START_TO_START, nodeStartRange) == 1;
48851         
48852          
48853     },
48854     rangeCompareNode : function(range, node)
48855     {
48856         var nodeRange = node.ownerDocument.createRange();
48857         try {
48858             nodeRange.selectNode(node);
48859         } catch (e) {
48860             nodeRange.selectNodeContents(node);
48861         }
48862         
48863         
48864         range.collapse(true);
48865     
48866         nodeRange.collapse(true);
48867      
48868         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
48869         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
48870          
48871         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
48872         
48873         var nodeIsBefore   =  ss == 1;
48874         var nodeIsAfter    = ee == -1;
48875         
48876         if (nodeIsBefore && nodeIsAfter) {
48877             return 0; // outer
48878         }
48879         if (!nodeIsBefore && nodeIsAfter) {
48880             return 1; //right trailed.
48881         }
48882         
48883         if (nodeIsBefore && !nodeIsAfter) {
48884             return 2;  // left trailed.
48885         }
48886         // fully contined.
48887         return 3;
48888     },
48889  
48890     cleanWordChars : function(input) {// change the chars to hex code
48891         
48892        var swapCodes  = [ 
48893             [    8211, "&#8211;" ], 
48894             [    8212, "&#8212;" ], 
48895             [    8216,  "'" ],  
48896             [    8217, "'" ],  
48897             [    8220, '"' ],  
48898             [    8221, '"' ],  
48899             [    8226, "*" ],  
48900             [    8230, "..." ]
48901         ]; 
48902         var output = input;
48903         Roo.each(swapCodes, function(sw) { 
48904             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
48905             
48906             output = output.replace(swapper, sw[1]);
48907         });
48908         
48909         return output;
48910     },
48911     
48912      
48913     
48914         
48915     
48916     cleanUpChild : function (node)
48917     {
48918         
48919         new Roo.htmleditor.FilterComment({node : node});
48920         new Roo.htmleditor.FilterAttributes({
48921                 node : node,
48922                 attrib_black : this.ablack,
48923                 attrib_clean : this.aclean,
48924                 style_white : this.cwhite,
48925                 style_black : this.cblack
48926         });
48927         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
48928         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
48929          
48930         
48931     },
48932     
48933     /**
48934      * Clean up MS wordisms...
48935      * @deprecated - use filter directly
48936      */
48937     cleanWord : function(node)
48938     {
48939         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
48940         
48941     },
48942    
48943     
48944     /**
48945
48946      * @deprecated - use filters
48947      */
48948     cleanTableWidths : function(node)
48949     {
48950         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
48951         
48952  
48953     },
48954     
48955      
48956         
48957     applyBlacklists : function()
48958     {
48959         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
48960         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
48961         
48962         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
48963         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
48964         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
48965         
48966         this.white = [];
48967         this.black = [];
48968         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
48969             if (b.indexOf(tag) > -1) {
48970                 return;
48971             }
48972             this.white.push(tag);
48973             
48974         }, this);
48975         
48976         Roo.each(w, function(tag) {
48977             if (b.indexOf(tag) > -1) {
48978                 return;
48979             }
48980             if (this.white.indexOf(tag) > -1) {
48981                 return;
48982             }
48983             this.white.push(tag);
48984             
48985         }, this);
48986         
48987         
48988         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
48989             if (w.indexOf(tag) > -1) {
48990                 return;
48991             }
48992             this.black.push(tag);
48993             
48994         }, this);
48995         
48996         Roo.each(b, function(tag) {
48997             if (w.indexOf(tag) > -1) {
48998                 return;
48999             }
49000             if (this.black.indexOf(tag) > -1) {
49001                 return;
49002             }
49003             this.black.push(tag);
49004             
49005         }, this);
49006         
49007         
49008         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
49009         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
49010         
49011         this.cwhite = [];
49012         this.cblack = [];
49013         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
49014             if (b.indexOf(tag) > -1) {
49015                 return;
49016             }
49017             this.cwhite.push(tag);
49018             
49019         }, this);
49020         
49021         Roo.each(w, function(tag) {
49022             if (b.indexOf(tag) > -1) {
49023                 return;
49024             }
49025             if (this.cwhite.indexOf(tag) > -1) {
49026                 return;
49027             }
49028             this.cwhite.push(tag);
49029             
49030         }, this);
49031         
49032         
49033         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
49034             if (w.indexOf(tag) > -1) {
49035                 return;
49036             }
49037             this.cblack.push(tag);
49038             
49039         }, this);
49040         
49041         Roo.each(b, function(tag) {
49042             if (w.indexOf(tag) > -1) {
49043                 return;
49044             }
49045             if (this.cblack.indexOf(tag) > -1) {
49046                 return;
49047             }
49048             this.cblack.push(tag);
49049             
49050         }, this);
49051     },
49052     
49053     setStylesheets : function(stylesheets)
49054     {
49055         if(typeof(stylesheets) == 'string'){
49056             Roo.get(this.iframe.contentDocument.head).createChild({
49057                 tag : 'link',
49058                 rel : 'stylesheet',
49059                 type : 'text/css',
49060                 href : stylesheets
49061             });
49062             
49063             return;
49064         }
49065         var _this = this;
49066      
49067         Roo.each(stylesheets, function(s) {
49068             if(!s.length){
49069                 return;
49070             }
49071             
49072             Roo.get(_this.iframe.contentDocument.head).createChild({
49073                 tag : 'link',
49074                 rel : 'stylesheet',
49075                 type : 'text/css',
49076                 href : s
49077             });
49078         });
49079
49080         
49081     },
49082     
49083     removeStylesheets : function()
49084     {
49085         var _this = this;
49086         
49087         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
49088             s.remove();
49089         });
49090     },
49091     
49092     setStyle : function(style)
49093     {
49094         Roo.get(this.iframe.contentDocument.head).createChild({
49095             tag : 'style',
49096             type : 'text/css',
49097             html : style
49098         });
49099
49100         return;
49101     }
49102     
49103     // hide stuff that is not compatible
49104     /**
49105      * @event blur
49106      * @hide
49107      */
49108     /**
49109      * @event change
49110      * @hide
49111      */
49112     /**
49113      * @event focus
49114      * @hide
49115      */
49116     /**
49117      * @event specialkey
49118      * @hide
49119      */
49120     /**
49121      * @cfg {String} fieldClass @hide
49122      */
49123     /**
49124      * @cfg {String} focusClass @hide
49125      */
49126     /**
49127      * @cfg {String} autoCreate @hide
49128      */
49129     /**
49130      * @cfg {String} inputType @hide
49131      */
49132     /**
49133      * @cfg {String} invalidClass @hide
49134      */
49135     /**
49136      * @cfg {String} invalidText @hide
49137      */
49138     /**
49139      * @cfg {String} msgFx @hide
49140      */
49141     /**
49142      * @cfg {String} validateOnBlur @hide
49143      */
49144 });
49145
49146 Roo.HtmlEditorCore.white = [
49147         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
49148         
49149        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
49150        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
49151        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
49152        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
49153        'TABLE',   'UL',         'XMP', 
49154        
49155        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
49156       'THEAD',   'TR', 
49157      
49158       'DIR', 'MENU', 'OL', 'UL', 'DL',
49159        
49160       'EMBED',  'OBJECT'
49161 ];
49162
49163
49164 Roo.HtmlEditorCore.black = [
49165     //    'embed',  'object', // enable - backend responsiblity to clean thiese
49166         'APPLET', // 
49167         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
49168         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
49169         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
49170         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
49171         //'FONT' // CLEAN LATER..
49172         'COLGROUP', 'COL'  // messy tables.
49173         
49174 ];
49175 Roo.HtmlEditorCore.clean = [ // ?? needed???
49176      'SCRIPT', 'STYLE', 'TITLE', 'XML'
49177 ];
49178 Roo.HtmlEditorCore.tag_remove = [
49179     'FONT', 'TBODY'  
49180 ];
49181 // attributes..
49182
49183 Roo.HtmlEditorCore.ablack = [
49184     'on'
49185 ];
49186     
49187 Roo.HtmlEditorCore.aclean = [ 
49188     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
49189 ];
49190
49191 // protocols..
49192 Roo.HtmlEditorCore.pwhite= [
49193         'http',  'https',  'mailto'
49194 ];
49195
49196 // white listed style attributes.
49197 Roo.HtmlEditorCore.cwhite= [
49198       //  'text-align', /// default is to allow most things..
49199       
49200          
49201 //        'font-size'//??
49202 ];
49203
49204 // black listed style attributes.
49205 Roo.HtmlEditorCore.cblack= [
49206       //  'font-size' -- this can be set by the project 
49207 ];
49208
49209
49210
49211
49212     //<script type="text/javascript">
49213
49214 /*
49215  * Ext JS Library 1.1.1
49216  * Copyright(c) 2006-2007, Ext JS, LLC.
49217  * Licence LGPL
49218  * 
49219  */
49220  
49221  
49222 Roo.form.HtmlEditor = function(config){
49223     
49224     
49225     
49226     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
49227     
49228     if (!this.toolbars) {
49229         this.toolbars = [];
49230     }
49231     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
49232     
49233     
49234 };
49235
49236 /**
49237  * @class Roo.form.HtmlEditor
49238  * @extends Roo.form.Field
49239  * Provides a lightweight HTML Editor component.
49240  *
49241  * This has been tested on Fireforx / Chrome.. IE may not be so great..
49242  * 
49243  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
49244  * supported by this editor.</b><br/><br/>
49245  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
49246  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
49247  */
49248 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
49249     /**
49250      * @cfg {Boolean} clearUp
49251      */
49252     clearUp : true,
49253       /**
49254      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
49255      */
49256     toolbars : false,
49257    
49258      /**
49259      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
49260      *                        Roo.resizable.
49261      */
49262     resizable : false,
49263      /**
49264      * @cfg {Number} height (in pixels)
49265      */   
49266     height: 300,
49267    /**
49268      * @cfg {Number} width (in pixels)
49269      */   
49270     width: 500,
49271     
49272     /**
49273      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
49274      * 
49275      */
49276     stylesheets: false,
49277     
49278     
49279      /**
49280      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
49281      * 
49282      */
49283     cblack: false,
49284     /**
49285      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
49286      * 
49287      */
49288     cwhite: false,
49289     
49290      /**
49291      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
49292      * 
49293      */
49294     black: false,
49295     /**
49296      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
49297      * 
49298      */
49299     white: false,
49300     /**
49301      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
49302      */
49303     allowComments: false,
49304     /**
49305      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
49306      */
49307     
49308     
49309      bodyCls : '',
49310     
49311     // id of frame..
49312     frameId: false,
49313     
49314     // private properties
49315     validationEvent : false,
49316     deferHeight: true,
49317     initialized : false,
49318     activated : false,
49319     
49320     onFocus : Roo.emptyFn,
49321     iframePad:3,
49322     hideMode:'offsets',
49323     
49324     actionMode : 'container', // defaults to hiding it...
49325     
49326     defaultAutoCreate : { // modified by initCompnoent..
49327         tag: "textarea",
49328         style:"width:500px;height:300px;",
49329         autocomplete: "new-password"
49330     },
49331
49332     // private
49333     initComponent : function(){
49334         this.addEvents({
49335             /**
49336              * @event initialize
49337              * Fires when the editor is fully initialized (including the iframe)
49338              * @param {HtmlEditor} this
49339              */
49340             initialize: true,
49341             /**
49342              * @event activate
49343              * Fires when the editor is first receives the focus. Any insertion must wait
49344              * until after this event.
49345              * @param {HtmlEditor} this
49346              */
49347             activate: true,
49348              /**
49349              * @event beforesync
49350              * Fires before the textarea is updated with content from the editor iframe. Return false
49351              * to cancel the sync.
49352              * @param {HtmlEditor} this
49353              * @param {String} html
49354              */
49355             beforesync: true,
49356              /**
49357              * @event beforepush
49358              * Fires before the iframe editor is updated with content from the textarea. Return false
49359              * to cancel the push.
49360              * @param {HtmlEditor} this
49361              * @param {String} html
49362              */
49363             beforepush: true,
49364              /**
49365              * @event sync
49366              * Fires when the textarea is updated with content from the editor iframe.
49367              * @param {HtmlEditor} this
49368              * @param {String} html
49369              */
49370             sync: true,
49371              /**
49372              * @event push
49373              * Fires when the iframe editor is updated with content from the textarea.
49374              * @param {HtmlEditor} this
49375              * @param {String} html
49376              */
49377             push: true,
49378              /**
49379              * @event editmodechange
49380              * Fires when the editor switches edit modes
49381              * @param {HtmlEditor} this
49382              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
49383              */
49384             editmodechange: true,
49385             /**
49386              * @event editorevent
49387              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
49388              * @param {HtmlEditor} this
49389              */
49390             editorevent: true,
49391             /**
49392              * @event firstfocus
49393              * Fires when on first focus - needed by toolbars..
49394              * @param {HtmlEditor} this
49395              */
49396             firstfocus: true,
49397             /**
49398              * @event autosave
49399              * Auto save the htmlEditor value as a file into Events
49400              * @param {HtmlEditor} this
49401              */
49402             autosave: true,
49403             /**
49404              * @event savedpreview
49405              * preview the saved version of htmlEditor
49406              * @param {HtmlEditor} this
49407              */
49408             savedpreview: true,
49409             
49410             /**
49411             * @event stylesheetsclick
49412             * Fires when press the Sytlesheets button
49413             * @param {Roo.HtmlEditorCore} this
49414             */
49415             stylesheetsclick: true,
49416             /**
49417             * @event paste
49418             * Fires when press user pastes into the editor
49419             * @param {Roo.HtmlEditorCore} this
49420             */
49421             paste: true 
49422         });
49423         this.defaultAutoCreate =  {
49424             tag: "textarea",
49425             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
49426             autocomplete: "new-password"
49427         };
49428     },
49429
49430     /**
49431      * Protected method that will not generally be called directly. It
49432      * is called when the editor creates its toolbar. Override this method if you need to
49433      * add custom toolbar buttons.
49434      * @param {HtmlEditor} editor
49435      */
49436     createToolbar : function(editor){
49437         Roo.log("create toolbars");
49438         if (!editor.toolbars || !editor.toolbars.length) {
49439             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
49440         }
49441         
49442         for (var i =0 ; i < editor.toolbars.length;i++) {
49443             editor.toolbars[i] = Roo.factory(
49444                     typeof(editor.toolbars[i]) == 'string' ?
49445                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
49446                 Roo.form.HtmlEditor);
49447             editor.toolbars[i].init(editor);
49448         }
49449          
49450         
49451     },
49452
49453      
49454     // private
49455     onRender : function(ct, position)
49456     {
49457         var _t = this;
49458         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
49459         
49460         this.wrap = this.el.wrap({
49461             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
49462         });
49463         
49464         this.editorcore.onRender(ct, position);
49465          
49466         if (this.resizable) {
49467             this.resizeEl = new Roo.Resizable(this.wrap, {
49468                 pinned : true,
49469                 wrap: true,
49470                 dynamic : true,
49471                 minHeight : this.height,
49472                 height: this.height,
49473                 handles : this.resizable,
49474                 width: this.width,
49475                 listeners : {
49476                     resize : function(r, w, h) {
49477                         _t.onResize(w,h); // -something
49478                     }
49479                 }
49480             });
49481             
49482         }
49483         this.createToolbar(this);
49484        
49485         
49486         if(!this.width){
49487             this.setSize(this.wrap.getSize());
49488         }
49489         if (this.resizeEl) {
49490             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
49491             // should trigger onReize..
49492         }
49493         
49494         this.keyNav = new Roo.KeyNav(this.el, {
49495             
49496             "tab" : function(e){
49497                 e.preventDefault();
49498                 
49499                 var value = this.getValue();
49500                 
49501                 var start = this.el.dom.selectionStart;
49502                 var end = this.el.dom.selectionEnd;
49503                 
49504                 if(!e.shiftKey){
49505                     
49506                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
49507                     this.el.dom.setSelectionRange(end + 1, end + 1);
49508                     return;
49509                 }
49510                 
49511                 var f = value.substring(0, start).split("\t");
49512                 
49513                 if(f.pop().length != 0){
49514                     return;
49515                 }
49516                 
49517                 this.setValue(f.join("\t") + value.substring(end));
49518                 this.el.dom.setSelectionRange(start - 1, start - 1);
49519                 
49520             },
49521             
49522             "home" : function(e){
49523                 e.preventDefault();
49524                 
49525                 var curr = this.el.dom.selectionStart;
49526                 var lines = this.getValue().split("\n");
49527                 
49528                 if(!lines.length){
49529                     return;
49530                 }
49531                 
49532                 if(e.ctrlKey){
49533                     this.el.dom.setSelectionRange(0, 0);
49534                     return;
49535                 }
49536                 
49537                 var pos = 0;
49538                 
49539                 for (var i = 0; i < lines.length;i++) {
49540                     pos += lines[i].length;
49541                     
49542                     if(i != 0){
49543                         pos += 1;
49544                     }
49545                     
49546                     if(pos < curr){
49547                         continue;
49548                     }
49549                     
49550                     pos -= lines[i].length;
49551                     
49552                     break;
49553                 }
49554                 
49555                 if(!e.shiftKey){
49556                     this.el.dom.setSelectionRange(pos, pos);
49557                     return;
49558                 }
49559                 
49560                 this.el.dom.selectionStart = pos;
49561                 this.el.dom.selectionEnd = curr;
49562             },
49563             
49564             "end" : function(e){
49565                 e.preventDefault();
49566                 
49567                 var curr = this.el.dom.selectionStart;
49568                 var lines = this.getValue().split("\n");
49569                 
49570                 if(!lines.length){
49571                     return;
49572                 }
49573                 
49574                 if(e.ctrlKey){
49575                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
49576                     return;
49577                 }
49578                 
49579                 var pos = 0;
49580                 
49581                 for (var i = 0; i < lines.length;i++) {
49582                     
49583                     pos += lines[i].length;
49584                     
49585                     if(i != 0){
49586                         pos += 1;
49587                     }
49588                     
49589                     if(pos < curr){
49590                         continue;
49591                     }
49592                     
49593                     break;
49594                 }
49595                 
49596                 if(!e.shiftKey){
49597                     this.el.dom.setSelectionRange(pos, pos);
49598                     return;
49599                 }
49600                 
49601                 this.el.dom.selectionStart = curr;
49602                 this.el.dom.selectionEnd = pos;
49603             },
49604
49605             scope : this,
49606
49607             doRelay : function(foo, bar, hname){
49608                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
49609             },
49610
49611             forceKeyDown: true
49612         });
49613         
49614 //        if(this.autosave && this.w){
49615 //            this.autoSaveFn = setInterval(this.autosave, 1000);
49616 //        }
49617     },
49618
49619     // private
49620     onResize : function(w, h)
49621     {
49622         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
49623         var ew = false;
49624         var eh = false;
49625         
49626         if(this.el ){
49627             if(typeof w == 'number'){
49628                 var aw = w - this.wrap.getFrameWidth('lr');
49629                 this.el.setWidth(this.adjustWidth('textarea', aw));
49630                 ew = aw;
49631             }
49632             if(typeof h == 'number'){
49633                 var tbh = 0;
49634                 for (var i =0; i < this.toolbars.length;i++) {
49635                     // fixme - ask toolbars for heights?
49636                     tbh += this.toolbars[i].tb.el.getHeight();
49637                     if (this.toolbars[i].footer) {
49638                         tbh += this.toolbars[i].footer.el.getHeight();
49639                     }
49640                 }
49641                 
49642                 
49643                 
49644                 
49645                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
49646                 ah -= 5; // knock a few pixes off for look..
49647 //                Roo.log(ah);
49648                 this.el.setHeight(this.adjustWidth('textarea', ah));
49649                 var eh = ah;
49650             }
49651         }
49652         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
49653         this.editorcore.onResize(ew,eh);
49654         
49655     },
49656
49657     /**
49658      * Toggles the editor between standard and source edit mode.
49659      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
49660      */
49661     toggleSourceEdit : function(sourceEditMode)
49662     {
49663         this.editorcore.toggleSourceEdit(sourceEditMode);
49664         
49665         if(this.editorcore.sourceEditMode){
49666             Roo.log('editor - showing textarea');
49667             
49668 //            Roo.log('in');
49669 //            Roo.log(this.syncValue());
49670             this.editorcore.syncValue();
49671             this.el.removeClass('x-hidden');
49672             this.el.dom.removeAttribute('tabIndex');
49673             this.el.focus();
49674             this.el.dom.scrollTop = 0;
49675             
49676             
49677             for (var i = 0; i < this.toolbars.length; i++) {
49678                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
49679                     this.toolbars[i].tb.hide();
49680                     this.toolbars[i].footer.hide();
49681                 }
49682             }
49683             
49684         }else{
49685             Roo.log('editor - hiding textarea');
49686 //            Roo.log('out')
49687 //            Roo.log(this.pushValue()); 
49688             this.editorcore.pushValue();
49689             
49690             this.el.addClass('x-hidden');
49691             this.el.dom.setAttribute('tabIndex', -1);
49692             
49693             for (var i = 0; i < this.toolbars.length; i++) {
49694                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
49695                     this.toolbars[i].tb.show();
49696                     this.toolbars[i].footer.show();
49697                 }
49698             }
49699             
49700             //this.deferFocus();
49701         }
49702         
49703         this.setSize(this.wrap.getSize());
49704         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
49705         
49706         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
49707     },
49708  
49709     // private (for BoxComponent)
49710     adjustSize : Roo.BoxComponent.prototype.adjustSize,
49711
49712     // private (for BoxComponent)
49713     getResizeEl : function(){
49714         return this.wrap;
49715     },
49716
49717     // private (for BoxComponent)
49718     getPositionEl : function(){
49719         return this.wrap;
49720     },
49721
49722     // private
49723     initEvents : function(){
49724         this.originalValue = this.getValue();
49725     },
49726
49727     /**
49728      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
49729      * @method
49730      */
49731     markInvalid : Roo.emptyFn,
49732     /**
49733      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
49734      * @method
49735      */
49736     clearInvalid : Roo.emptyFn,
49737
49738     setValue : function(v){
49739         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
49740         this.editorcore.pushValue();
49741     },
49742
49743      
49744     // private
49745     deferFocus : function(){
49746         this.focus.defer(10, this);
49747     },
49748
49749     // doc'ed in Field
49750     focus : function(){
49751         this.editorcore.focus();
49752         
49753     },
49754       
49755
49756     // private
49757     onDestroy : function(){
49758         
49759         
49760         
49761         if(this.rendered){
49762             
49763             for (var i =0; i < this.toolbars.length;i++) {
49764                 // fixme - ask toolbars for heights?
49765                 this.toolbars[i].onDestroy();
49766             }
49767             
49768             this.wrap.dom.innerHTML = '';
49769             this.wrap.remove();
49770         }
49771     },
49772
49773     // private
49774     onFirstFocus : function(){
49775         //Roo.log("onFirstFocus");
49776         this.editorcore.onFirstFocus();
49777          for (var i =0; i < this.toolbars.length;i++) {
49778             this.toolbars[i].onFirstFocus();
49779         }
49780         
49781     },
49782     
49783     // private
49784     syncValue : function()
49785     {
49786         this.editorcore.syncValue();
49787     },
49788     
49789     pushValue : function()
49790     {
49791         this.editorcore.pushValue();
49792     },
49793     
49794     setStylesheets : function(stylesheets)
49795     {
49796         this.editorcore.setStylesheets(stylesheets);
49797     },
49798     
49799     removeStylesheets : function()
49800     {
49801         this.editorcore.removeStylesheets();
49802     }
49803      
49804     
49805     // hide stuff that is not compatible
49806     /**
49807      * @event blur
49808      * @hide
49809      */
49810     /**
49811      * @event change
49812      * @hide
49813      */
49814     /**
49815      * @event focus
49816      * @hide
49817      */
49818     /**
49819      * @event specialkey
49820      * @hide
49821      */
49822     /**
49823      * @cfg {String} fieldClass @hide
49824      */
49825     /**
49826      * @cfg {String} focusClass @hide
49827      */
49828     /**
49829      * @cfg {String} autoCreate @hide
49830      */
49831     /**
49832      * @cfg {String} inputType @hide
49833      */
49834     /**
49835      * @cfg {String} invalidClass @hide
49836      */
49837     /**
49838      * @cfg {String} invalidText @hide
49839      */
49840     /**
49841      * @cfg {String} msgFx @hide
49842      */
49843     /**
49844      * @cfg {String} validateOnBlur @hide
49845      */
49846 });
49847  
49848     // <script type="text/javascript">
49849 /*
49850  * Based on
49851  * Ext JS Library 1.1.1
49852  * Copyright(c) 2006-2007, Ext JS, LLC.
49853  *  
49854  
49855  */
49856
49857 /**
49858  * @class Roo.form.HtmlEditorToolbar1
49859  * Basic Toolbar
49860  * 
49861  * Usage:
49862  *
49863  new Roo.form.HtmlEditor({
49864     ....
49865     toolbars : [
49866         new Roo.form.HtmlEditorToolbar1({
49867             disable : { fonts: 1 , format: 1, ..., ... , ...],
49868             btns : [ .... ]
49869         })
49870     }
49871      
49872  * 
49873  * @cfg {Object} disable List of elements to disable..
49874  * @cfg {Array} btns List of additional buttons.
49875  * 
49876  * 
49877  * NEEDS Extra CSS? 
49878  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
49879  */
49880  
49881 Roo.form.HtmlEditor.ToolbarStandard = function(config)
49882 {
49883     
49884     Roo.apply(this, config);
49885     
49886     // default disabled, based on 'good practice'..
49887     this.disable = this.disable || {};
49888     Roo.applyIf(this.disable, {
49889         fontSize : true,
49890         colors : true,
49891         specialElements : true
49892     });
49893     
49894     
49895     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49896     // dont call parent... till later.
49897 }
49898
49899 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
49900     
49901     tb: false,
49902     
49903     rendered: false,
49904     
49905     editor : false,
49906     editorcore : false,
49907     /**
49908      * @cfg {Object} disable  List of toolbar elements to disable
49909          
49910      */
49911     disable : false,
49912     
49913     
49914      /**
49915      * @cfg {String} createLinkText The default text for the create link prompt
49916      */
49917     createLinkText : 'Please enter the URL for the link:',
49918     /**
49919      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
49920      */
49921     defaultLinkValue : 'http:/'+'/',
49922    
49923     
49924       /**
49925      * @cfg {Array} fontFamilies An array of available font families
49926      */
49927     fontFamilies : [
49928         'Arial',
49929         'Courier New',
49930         'Tahoma',
49931         'Times New Roman',
49932         'Verdana'
49933     ],
49934     
49935     specialChars : [
49936            "&#169;",
49937           "&#174;",     
49938           "&#8482;",    
49939           "&#163;" ,    
49940          // "&#8212;",    
49941           "&#8230;",    
49942           "&#247;" ,    
49943         //  "&#225;" ,     ?? a acute?
49944            "&#8364;"    , //Euro
49945        //   "&#8220;"    ,
49946         //  "&#8221;"    ,
49947         //  "&#8226;"    ,
49948           "&#176;"  //   , // degrees
49949
49950          // "&#233;"     , // e ecute
49951          // "&#250;"     , // u ecute?
49952     ],
49953     
49954     specialElements : [
49955         {
49956             text: "Insert Table",
49957             xtype: 'MenuItem',
49958             xns : Roo.Menu,
49959             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
49960                 
49961         },
49962         {    
49963             text: "Insert Image",
49964             xtype: 'MenuItem',
49965             xns : Roo.Menu,
49966             ihtml : '<img src="about:blank"/>'
49967             
49968         }
49969         
49970          
49971     ],
49972     
49973     
49974     inputElements : [ 
49975             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
49976             "input:submit", "input:button", "select", "textarea", "label" ],
49977     formats : [
49978         ["p"] ,  
49979         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
49980         ["pre"],[ "code"], 
49981         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
49982         ['div'],['span'],
49983         ['sup'],['sub']
49984     ],
49985     
49986     cleanStyles : [
49987         "font-size"
49988     ],
49989      /**
49990      * @cfg {String} defaultFont default font to use.
49991      */
49992     defaultFont: 'tahoma',
49993    
49994     fontSelect : false,
49995     
49996     
49997     formatCombo : false,
49998     
49999     init : function(editor)
50000     {
50001         this.editor = editor;
50002         this.editorcore = editor.editorcore ? editor.editorcore : editor;
50003         var editorcore = this.editorcore;
50004         
50005         var _t = this;
50006         
50007         var fid = editorcore.frameId;
50008         var etb = this;
50009         function btn(id, toggle, handler){
50010             var xid = fid + '-'+ id ;
50011             return {
50012                 id : xid,
50013                 cmd : id,
50014                 cls : 'x-btn-icon x-edit-'+id,
50015                 enableToggle:toggle !== false,
50016                 scope: _t, // was editor...
50017                 handler:handler||_t.relayBtnCmd,
50018                 clickEvent:'mousedown',
50019                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50020                 tabIndex:-1
50021             };
50022         }
50023         
50024         
50025         
50026         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50027         this.tb = tb;
50028          // stop form submits
50029         tb.el.on('click', function(e){
50030             e.preventDefault(); // what does this do?
50031         });
50032
50033         if(!this.disable.font) { // && !Roo.isSafari){
50034             /* why no safari for fonts 
50035             editor.fontSelect = tb.el.createChild({
50036                 tag:'select',
50037                 tabIndex: -1,
50038                 cls:'x-font-select',
50039                 html: this.createFontOptions()
50040             });
50041             
50042             editor.fontSelect.on('change', function(){
50043                 var font = editor.fontSelect.dom.value;
50044                 editor.relayCmd('fontname', font);
50045                 editor.deferFocus();
50046             }, editor);
50047             
50048             tb.add(
50049                 editor.fontSelect.dom,
50050                 '-'
50051             );
50052             */
50053             
50054         };
50055         if(!this.disable.formats){
50056             this.formatCombo = new Roo.form.ComboBox({
50057                 store: new Roo.data.SimpleStore({
50058                     id : 'tag',
50059                     fields: ['tag'],
50060                     data : this.formats // from states.js
50061                 }),
50062                 blockFocus : true,
50063                 name : '',
50064                 //autoCreate : {tag: "div",  size: "20"},
50065                 displayField:'tag',
50066                 typeAhead: false,
50067                 mode: 'local',
50068                 editable : false,
50069                 triggerAction: 'all',
50070                 emptyText:'Add tag',
50071                 selectOnFocus:true,
50072                 width:135,
50073                 listeners : {
50074                     'select': function(c, r, i) {
50075                         editorcore.insertTag(r.get('tag'));
50076                         editor.focus();
50077                     }
50078                 }
50079
50080             });
50081             tb.addField(this.formatCombo);
50082             
50083         }
50084         
50085         if(!this.disable.format){
50086             tb.add(
50087                 btn('bold'),
50088                 btn('italic'),
50089                 btn('underline'),
50090                 btn('strikethrough')
50091             );
50092         };
50093         if(!this.disable.fontSize){
50094             tb.add(
50095                 '-',
50096                 
50097                 
50098                 btn('increasefontsize', false, editorcore.adjustFont),
50099                 btn('decreasefontsize', false, editorcore.adjustFont)
50100             );
50101         };
50102         
50103         
50104         if(!this.disable.colors){
50105             tb.add(
50106                 '-', {
50107                     id:editorcore.frameId +'-forecolor',
50108                     cls:'x-btn-icon x-edit-forecolor',
50109                     clickEvent:'mousedown',
50110                     tooltip: this.buttonTips['forecolor'] || undefined,
50111                     tabIndex:-1,
50112                     menu : new Roo.menu.ColorMenu({
50113                         allowReselect: true,
50114                         focus: Roo.emptyFn,
50115                         value:'000000',
50116                         plain:true,
50117                         selectHandler: function(cp, color){
50118                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
50119                             editor.deferFocus();
50120                         },
50121                         scope: editorcore,
50122                         clickEvent:'mousedown'
50123                     })
50124                 }, {
50125                     id:editorcore.frameId +'backcolor',
50126                     cls:'x-btn-icon x-edit-backcolor',
50127                     clickEvent:'mousedown',
50128                     tooltip: this.buttonTips['backcolor'] || undefined,
50129                     tabIndex:-1,
50130                     menu : new Roo.menu.ColorMenu({
50131                         focus: Roo.emptyFn,
50132                         value:'FFFFFF',
50133                         plain:true,
50134                         allowReselect: true,
50135                         selectHandler: function(cp, color){
50136                             if(Roo.isGecko){
50137                                 editorcore.execCmd('useCSS', false);
50138                                 editorcore.execCmd('hilitecolor', color);
50139                                 editorcore.execCmd('useCSS', true);
50140                                 editor.deferFocus();
50141                             }else{
50142                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
50143                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
50144                                 editor.deferFocus();
50145                             }
50146                         },
50147                         scope:editorcore,
50148                         clickEvent:'mousedown'
50149                     })
50150                 }
50151             );
50152         };
50153         // now add all the items...
50154         
50155
50156         if(!this.disable.alignments){
50157             tb.add(
50158                 '-',
50159                 btn('justifyleft'),
50160                 btn('justifycenter'),
50161                 btn('justifyright')
50162             );
50163         };
50164
50165         //if(!Roo.isSafari){
50166             if(!this.disable.links){
50167                 tb.add(
50168                     '-',
50169                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
50170                 );
50171             };
50172
50173             if(!this.disable.lists){
50174                 tb.add(
50175                     '-',
50176                     btn('insertorderedlist'),
50177                     btn('insertunorderedlist')
50178                 );
50179             }
50180             if(!this.disable.sourceEdit){
50181                 tb.add(
50182                     '-',
50183                     btn('sourceedit', true, function(btn){
50184                         this.toggleSourceEdit(btn.pressed);
50185                     })
50186                 );
50187             }
50188         //}
50189         
50190         var smenu = { };
50191         // special menu.. - needs to be tidied up..
50192         if (!this.disable.special) {
50193             smenu = {
50194                 text: "&#169;",
50195                 cls: 'x-edit-none',
50196                 
50197                 menu : {
50198                     items : []
50199                 }
50200             };
50201             for (var i =0; i < this.specialChars.length; i++) {
50202                 smenu.menu.items.push({
50203                     
50204                     html: this.specialChars[i],
50205                     handler: function(a,b) {
50206                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
50207                         //editor.insertAtCursor(a.html);
50208                         
50209                     },
50210                     tabIndex:-1
50211                 });
50212             }
50213             
50214             
50215             tb.add(smenu);
50216             
50217             
50218         }
50219         
50220         var cmenu = { };
50221         if (!this.disable.cleanStyles) {
50222             cmenu = {
50223                 cls: 'x-btn-icon x-btn-clear',
50224                 
50225                 menu : {
50226                     items : []
50227                 }
50228             };
50229             for (var i =0; i < this.cleanStyles.length; i++) {
50230                 cmenu.menu.items.push({
50231                     actiontype : this.cleanStyles[i],
50232                     html: 'Remove ' + this.cleanStyles[i],
50233                     handler: function(a,b) {
50234 //                        Roo.log(a);
50235 //                        Roo.log(b);
50236                         var c = Roo.get(editorcore.doc.body);
50237                         c.select('[style]').each(function(s) {
50238                             s.dom.style.removeProperty(a.actiontype);
50239                         });
50240                         editorcore.syncValue();
50241                     },
50242                     tabIndex:-1
50243                 });
50244             }
50245             cmenu.menu.items.push({
50246                 actiontype : 'tablewidths',
50247                 html: 'Remove Table Widths',
50248                 handler: function(a,b) {
50249                     editorcore.cleanTableWidths();
50250                     editorcore.syncValue();
50251                 },
50252                 tabIndex:-1
50253             });
50254             cmenu.menu.items.push({
50255                 actiontype : 'word',
50256                 html: 'Remove MS Word Formating',
50257                 handler: function(a,b) {
50258                     editorcore.cleanWord();
50259                     editorcore.syncValue();
50260                 },
50261                 tabIndex:-1
50262             });
50263             
50264             cmenu.menu.items.push({
50265                 actiontype : 'all',
50266                 html: 'Remove All Styles',
50267                 handler: function(a,b) {
50268                     
50269                     var c = Roo.get(editorcore.doc.body);
50270                     c.select('[style]').each(function(s) {
50271                         s.dom.removeAttribute('style');
50272                     });
50273                     editorcore.syncValue();
50274                 },
50275                 tabIndex:-1
50276             });
50277             
50278             cmenu.menu.items.push({
50279                 actiontype : 'all',
50280                 html: 'Remove All CSS Classes',
50281                 handler: function(a,b) {
50282                     
50283                     var c = Roo.get(editorcore.doc.body);
50284                     c.select('[class]').each(function(s) {
50285                         s.dom.removeAttribute('class');
50286                     });
50287                     editorcore.cleanWord();
50288                     editorcore.syncValue();
50289                 },
50290                 tabIndex:-1
50291             });
50292             
50293              cmenu.menu.items.push({
50294                 actiontype : 'tidy',
50295                 html: 'Tidy HTML Source',
50296                 handler: function(a,b) {
50297                     new Roo.htmleditor.Tidy(editorcore.doc.body);
50298                     editorcore.syncValue();
50299                 },
50300                 tabIndex:-1
50301             });
50302             
50303             
50304             tb.add(cmenu);
50305         }
50306          
50307         if (!this.disable.specialElements) {
50308             var semenu = {
50309                 text: "Other;",
50310                 cls: 'x-edit-none',
50311                 menu : {
50312                     items : []
50313                 }
50314             };
50315             for (var i =0; i < this.specialElements.length; i++) {
50316                 semenu.menu.items.push(
50317                     Roo.apply({ 
50318                         handler: function(a,b) {
50319                             editor.insertAtCursor(this.ihtml);
50320                         }
50321                     }, this.specialElements[i])
50322                 );
50323                     
50324             }
50325             
50326             tb.add(semenu);
50327             
50328             
50329         }
50330          
50331         
50332         if (this.btns) {
50333             for(var i =0; i< this.btns.length;i++) {
50334                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
50335                 b.cls =  'x-edit-none';
50336                 
50337                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
50338                     b.cls += ' x-init-enable';
50339                 }
50340                 
50341                 b.scope = editorcore;
50342                 tb.add(b);
50343             }
50344         
50345         }
50346         
50347         
50348         
50349         // disable everything...
50350         
50351         this.tb.items.each(function(item){
50352             
50353            if(
50354                 item.id != editorcore.frameId+ '-sourceedit' && 
50355                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
50356             ){
50357                 
50358                 item.disable();
50359             }
50360         });
50361         this.rendered = true;
50362         
50363         // the all the btns;
50364         editor.on('editorevent', this.updateToolbar, this);
50365         // other toolbars need to implement this..
50366         //editor.on('editmodechange', this.updateToolbar, this);
50367     },
50368     
50369     
50370     relayBtnCmd : function(btn) {
50371         this.editorcore.relayCmd(btn.cmd);
50372     },
50373     // private used internally
50374     createLink : function(){
50375         Roo.log("create link?");
50376         var url = prompt(this.createLinkText, this.defaultLinkValue);
50377         if(url && url != 'http:/'+'/'){
50378             this.editorcore.relayCmd('createlink', url);
50379         }
50380     },
50381
50382     
50383     /**
50384      * Protected method that will not generally be called directly. It triggers
50385      * a toolbar update by reading the markup state of the current selection in the editor.
50386      */
50387     updateToolbar: function(){
50388
50389         if(!this.editorcore.activated){
50390             this.editor.onFirstFocus();
50391             return;
50392         }
50393
50394         var btns = this.tb.items.map, 
50395             doc = this.editorcore.doc,
50396             frameId = this.editorcore.frameId;
50397
50398         if(!this.disable.font && !Roo.isSafari){
50399             /*
50400             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
50401             if(name != this.fontSelect.dom.value){
50402                 this.fontSelect.dom.value = name;
50403             }
50404             */
50405         }
50406         if(!this.disable.format){
50407             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
50408             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
50409             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
50410             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
50411         }
50412         if(!this.disable.alignments){
50413             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
50414             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
50415             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
50416         }
50417         if(!Roo.isSafari && !this.disable.lists){
50418             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
50419             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
50420         }
50421         
50422         var ans = this.editorcore.getAllAncestors();
50423         if (this.formatCombo) {
50424             
50425             
50426             var store = this.formatCombo.store;
50427             this.formatCombo.setValue("");
50428             for (var i =0; i < ans.length;i++) {
50429                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
50430                     // select it..
50431                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
50432                     break;
50433                 }
50434             }
50435         }
50436         
50437         
50438         
50439         // hides menus... - so this cant be on a menu...
50440         Roo.menu.MenuMgr.hideAll();
50441
50442         //this.editorsyncValue();
50443     },
50444    
50445     
50446     createFontOptions : function(){
50447         var buf = [], fs = this.fontFamilies, ff, lc;
50448         
50449         
50450         
50451         for(var i = 0, len = fs.length; i< len; i++){
50452             ff = fs[i];
50453             lc = ff.toLowerCase();
50454             buf.push(
50455                 '<option value="',lc,'" style="font-family:',ff,';"',
50456                     (this.defaultFont == lc ? ' selected="true">' : '>'),
50457                     ff,
50458                 '</option>'
50459             );
50460         }
50461         return buf.join('');
50462     },
50463     
50464     toggleSourceEdit : function(sourceEditMode){
50465         
50466         Roo.log("toolbar toogle");
50467         if(sourceEditMode === undefined){
50468             sourceEditMode = !this.sourceEditMode;
50469         }
50470         this.sourceEditMode = sourceEditMode === true;
50471         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
50472         // just toggle the button?
50473         if(btn.pressed !== this.sourceEditMode){
50474             btn.toggle(this.sourceEditMode);
50475             return;
50476         }
50477         
50478         if(sourceEditMode){
50479             Roo.log("disabling buttons");
50480             this.tb.items.each(function(item){
50481                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
50482                     item.disable();
50483                 }
50484             });
50485           
50486         }else{
50487             Roo.log("enabling buttons");
50488             if(this.editorcore.initialized){
50489                 this.tb.items.each(function(item){
50490                     item.enable();
50491                 });
50492                 // initialize 'blocks'
50493                 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
50494                     Roo.htmleditor.Block.factory(e).updateElement(e);
50495                 },this);
50496             
50497             }
50498             
50499         }
50500         Roo.log("calling toggole on editor");
50501         // tell the editor that it's been pressed..
50502         this.editor.toggleSourceEdit(sourceEditMode);
50503        
50504     },
50505      /**
50506      * Object collection of toolbar tooltips for the buttons in the editor. The key
50507      * is the command id associated with that button and the value is a valid QuickTips object.
50508      * For example:
50509 <pre><code>
50510 {
50511     bold : {
50512         title: 'Bold (Ctrl+B)',
50513         text: 'Make the selected text bold.',
50514         cls: 'x-html-editor-tip'
50515     },
50516     italic : {
50517         title: 'Italic (Ctrl+I)',
50518         text: 'Make the selected text italic.',
50519         cls: 'x-html-editor-tip'
50520     },
50521     ...
50522 </code></pre>
50523     * @type Object
50524      */
50525     buttonTips : {
50526         bold : {
50527             title: 'Bold (Ctrl+B)',
50528             text: 'Make the selected text bold.',
50529             cls: 'x-html-editor-tip'
50530         },
50531         italic : {
50532             title: 'Italic (Ctrl+I)',
50533             text: 'Make the selected text italic.',
50534             cls: 'x-html-editor-tip'
50535         },
50536         underline : {
50537             title: 'Underline (Ctrl+U)',
50538             text: 'Underline the selected text.',
50539             cls: 'x-html-editor-tip'
50540         },
50541         strikethrough : {
50542             title: 'Strikethrough',
50543             text: 'Strikethrough the selected text.',
50544             cls: 'x-html-editor-tip'
50545         },
50546         increasefontsize : {
50547             title: 'Grow Text',
50548             text: 'Increase the font size.',
50549             cls: 'x-html-editor-tip'
50550         },
50551         decreasefontsize : {
50552             title: 'Shrink Text',
50553             text: 'Decrease the font size.',
50554             cls: 'x-html-editor-tip'
50555         },
50556         backcolor : {
50557             title: 'Text Highlight Color',
50558             text: 'Change the background color of the selected text.',
50559             cls: 'x-html-editor-tip'
50560         },
50561         forecolor : {
50562             title: 'Font Color',
50563             text: 'Change the color of the selected text.',
50564             cls: 'x-html-editor-tip'
50565         },
50566         justifyleft : {
50567             title: 'Align Text Left',
50568             text: 'Align text to the left.',
50569             cls: 'x-html-editor-tip'
50570         },
50571         justifycenter : {
50572             title: 'Center Text',
50573             text: 'Center text in the editor.',
50574             cls: 'x-html-editor-tip'
50575         },
50576         justifyright : {
50577             title: 'Align Text Right',
50578             text: 'Align text to the right.',
50579             cls: 'x-html-editor-tip'
50580         },
50581         insertunorderedlist : {
50582             title: 'Bullet List',
50583             text: 'Start a bulleted list.',
50584             cls: 'x-html-editor-tip'
50585         },
50586         insertorderedlist : {
50587             title: 'Numbered List',
50588             text: 'Start a numbered list.',
50589             cls: 'x-html-editor-tip'
50590         },
50591         createlink : {
50592             title: 'Hyperlink',
50593             text: 'Make the selected text a hyperlink.',
50594             cls: 'x-html-editor-tip'
50595         },
50596         sourceedit : {
50597             title: 'Source Edit',
50598             text: 'Switch to source editing mode.',
50599             cls: 'x-html-editor-tip'
50600         }
50601     },
50602     // private
50603     onDestroy : function(){
50604         if(this.rendered){
50605             
50606             this.tb.items.each(function(item){
50607                 if(item.menu){
50608                     item.menu.removeAll();
50609                     if(item.menu.el){
50610                         item.menu.el.destroy();
50611                     }
50612                 }
50613                 item.destroy();
50614             });
50615              
50616         }
50617     },
50618     onFirstFocus: function() {
50619         this.tb.items.each(function(item){
50620            item.enable();
50621         });
50622     }
50623 });
50624
50625
50626
50627
50628 // <script type="text/javascript">
50629 /*
50630  * Based on
50631  * Ext JS Library 1.1.1
50632  * Copyright(c) 2006-2007, Ext JS, LLC.
50633  *  
50634  
50635  */
50636
50637  
50638 /**
50639  * @class Roo.form.HtmlEditor.ToolbarContext
50640  * Context Toolbar
50641  * 
50642  * Usage:
50643  *
50644  new Roo.form.HtmlEditor({
50645     ....
50646     toolbars : [
50647         { xtype: 'ToolbarStandard', styles : {} }
50648         { xtype: 'ToolbarContext', disable : {} }
50649     ]
50650 })
50651
50652      
50653  * 
50654  * @config : {Object} disable List of elements to disable.. (not done yet.)
50655  * @config : {Object} styles  Map of styles available.
50656  * 
50657  */
50658
50659 Roo.form.HtmlEditor.ToolbarContext = function(config)
50660 {
50661     
50662     Roo.apply(this, config);
50663     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
50664     // dont call parent... till later.
50665     this.styles = this.styles || {};
50666 }
50667
50668  
50669
50670 Roo.form.HtmlEditor.ToolbarContext.types = {
50671     'IMG' : [
50672         {
50673             name : 'width',
50674             title: "Width",
50675             width: 40
50676         },
50677         {
50678             name : 'height',
50679             title: "Height",
50680             width: 40
50681         },
50682         {
50683             name : 'align',
50684             title: "Align",
50685             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
50686             width : 80
50687             
50688         },
50689         {
50690             name : 'border',
50691             title: "Border",
50692             width: 40
50693         },
50694         {
50695             name : 'alt',
50696             title: "Alt",
50697             width: 120
50698         },
50699         {
50700             name : 'src',
50701             title: "Src",
50702             width: 220
50703         }
50704         
50705     ],
50706     
50707     'FIGURE' : [
50708         {
50709             name : 'align',
50710             title: "Align",
50711             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
50712             width : 80  
50713         }
50714     ],
50715     'A' : [
50716         {
50717             name : 'name',
50718             title: "Name",
50719             width: 50
50720         },
50721         {
50722             name : 'target',
50723             title: "Target",
50724             width: 120
50725         },
50726         {
50727             name : 'href',
50728             title: "Href",
50729             width: 220
50730         } // border?
50731         
50732     ],
50733     
50734     'INPUT' : [
50735         {
50736             name : 'name',
50737             title: "name",
50738             width: 120
50739         },
50740         {
50741             name : 'value',
50742             title: "Value",
50743             width: 120
50744         },
50745         {
50746             name : 'width',
50747             title: "Width",
50748             width: 40
50749         }
50750     ],
50751     'LABEL' : [
50752          {
50753             name : 'for',
50754             title: "For",
50755             width: 120
50756         }
50757     ],
50758     'TEXTAREA' : [
50759         {
50760             name : 'name',
50761             title: "name",
50762             width: 120
50763         },
50764         {
50765             name : 'rows',
50766             title: "Rows",
50767             width: 20
50768         },
50769         {
50770             name : 'cols',
50771             title: "Cols",
50772             width: 20
50773         }
50774     ],
50775     'SELECT' : [
50776         {
50777             name : 'name',
50778             title: "name",
50779             width: 120
50780         },
50781         {
50782             name : 'selectoptions',
50783             title: "Options",
50784             width: 200
50785         }
50786     ],
50787     
50788     // should we really allow this??
50789     // should this just be 
50790     'BODY' : [
50791         
50792         {
50793             name : 'title',
50794             title: "Title",
50795             width: 200,
50796             disabled : true
50797         }
50798     ],
50799  
50800     '*' : [
50801         // empty.
50802     ]
50803
50804 };
50805
50806 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
50807 Roo.form.HtmlEditor.ToolbarContext.stores = false;
50808
50809 Roo.form.HtmlEditor.ToolbarContext.options = {
50810         'font-family'  : [ 
50811                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
50812                 [ 'Courier New', 'Courier New'],
50813                 [ 'Tahoma', 'Tahoma'],
50814                 [ 'Times New Roman,serif', 'Times'],
50815                 [ 'Verdana','Verdana' ]
50816         ]
50817 };
50818
50819 // fixme - these need to be configurable..
50820  
50821
50822 //Roo.form.HtmlEditor.ToolbarContext.types
50823
50824
50825 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
50826     
50827     tb: false,
50828     
50829     rendered: false,
50830     
50831     editor : false,
50832     editorcore : false,
50833     /**
50834      * @cfg {Object} disable  List of toolbar elements to disable
50835          
50836      */
50837     disable : false,
50838     /**
50839      * @cfg {Object} styles List of styles 
50840      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
50841      *
50842      * These must be defined in the page, so they get rendered correctly..
50843      * .headline { }
50844      * TD.underline { }
50845      * 
50846      */
50847     styles : false,
50848     
50849     options: false,
50850     
50851     toolbars : false,
50852     
50853     init : function(editor)
50854     {
50855         this.editor = editor;
50856         this.editorcore = editor.editorcore ? editor.editorcore : editor;
50857         var editorcore = this.editorcore;
50858         
50859         var fid = editorcore.frameId;
50860         var etb = this;
50861         function btn(id, toggle, handler){
50862             var xid = fid + '-'+ id ;
50863             return {
50864                 id : xid,
50865                 cmd : id,
50866                 cls : 'x-btn-icon x-edit-'+id,
50867                 enableToggle:toggle !== false,
50868                 scope: editorcore, // was editor...
50869                 handler:handler||editorcore.relayBtnCmd,
50870                 clickEvent:'mousedown',
50871                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50872                 tabIndex:-1
50873             };
50874         }
50875         // create a new element.
50876         var wdiv = editor.wrap.createChild({
50877                 tag: 'div'
50878             }, editor.wrap.dom.firstChild.nextSibling, true);
50879         
50880         // can we do this more than once??
50881         
50882          // stop form submits
50883       
50884  
50885         // disable everything...
50886         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
50887         this.toolbars = {};
50888            
50889         for (var i in  ty) {
50890           
50891             this.toolbars[i] = this.buildToolbar(ty[i],i);
50892         }
50893         this.tb = this.toolbars.BODY;
50894         this.tb.el.show();
50895         this.buildFooter();
50896         this.footer.show();
50897         editor.on('hide', function( ) { this.footer.hide() }, this);
50898         editor.on('show', function( ) { this.footer.show() }, this);
50899         
50900          
50901         this.rendered = true;
50902         
50903         // the all the btns;
50904         editor.on('editorevent', this.updateToolbar, this);
50905         // other toolbars need to implement this..
50906         //editor.on('editmodechange', this.updateToolbar, this);
50907     },
50908     
50909     
50910     
50911     /**
50912      * Protected method that will not generally be called directly. It triggers
50913      * a toolbar update by reading the markup state of the current selection in the editor.
50914      *
50915      * Note you can force an update by calling on('editorevent', scope, false)
50916      */
50917     updateToolbar: function(editor ,ev, sel)
50918     {
50919         
50920         if (ev) {
50921             ev.stopEvent(); // se if we can stop this looping with mutiple events.
50922         }
50923         
50924         //Roo.log(ev);
50925         // capture mouse up - this is handy for selecting images..
50926         // perhaps should go somewhere else...
50927         if(!this.editorcore.activated){
50928              this.editor.onFirstFocus();
50929             return;
50930         }
50931         //Roo.log(ev ? ev.target : 'NOTARGET');
50932         
50933         
50934         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
50935         // selectNode - might want to handle IE?
50936         
50937         
50938         
50939         if (ev &&
50940             (ev.type == 'mouseup' || ev.type == 'click' ) &&
50941             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
50942             // they have click on an image...
50943             // let's see if we can change the selection...
50944             sel = ev.target;
50945             
50946             // this triggers looping?
50947             //this.editorcore.selectNode(sel);
50948              
50949         }  
50950         Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
50951         //Roo.get(node).addClass('roo-ed-selection');
50952       
50953         //var updateFooter = sel ? false : true; 
50954         
50955         
50956         var ans = this.editorcore.getAllAncestors();
50957         
50958         // pick
50959         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
50960         
50961         if (!sel) { 
50962             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
50963             sel = sel ? sel : this.editorcore.doc.body;
50964             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
50965             
50966         }
50967         
50968         var tn = sel.tagName.toUpperCase();
50969         var lastSel = this.tb.selectedNode;
50970         this.tb.selectedNode = sel;
50971         var left_label = tn;
50972         
50973         // ok see if we are editing a block?
50974         
50975         var db = false;
50976         // you are not actually selecting the block.
50977         if (sel && sel.hasAttribute('data-block')) {
50978             db = sel;
50979         } else if (sel && !sel.hasAttribute('contenteditable')) {
50980             var sel_el = Roo.get(sel);
50981             db = sel_el.findParent('[data-block]');
50982             var cepar = sel_el.findParent('[contenteditable=true]');
50983             if (db && cepar && cepar.tagName != 'BODY') {
50984                db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
50985             }   
50986         }
50987         
50988         
50989         var block = false;
50990         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
50991         if (db) {
50992             block = Roo.htmleditor.Block.factory(db);
50993             
50994             
50995             if (block) {
50996                 db.className +=  ' roo-ed-selection'; // since we removed it earlier... its not there..
50997                 tn = 'BLOCK.' + db.getAttribute('data-block');
50998                 
50999                 //this.editorcore.selectNode(db);
51000                 if (typeof(this.toolbars[tn]) == 'undefined') {
51001                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
51002                 }
51003                 this.toolbars[tn].selectedNode = db;
51004                 left_label = block.friendly_name;
51005                 ans = this.editorcore.getAllAncestors();
51006             }
51007             
51008                 
51009             
51010         }
51011         
51012         
51013         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
51014             return; // no change?
51015         }
51016         
51017         
51018           
51019         this.tb.el.hide();
51020         ///console.log("show: " + tn);
51021         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
51022         
51023         this.tb.el.show();
51024         // update name
51025         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
51026         
51027         
51028         // update attributes
51029         if (block && this.tb.fields) {
51030              
51031             this.tb.fields.each(function(e) {
51032                 e.setValue(block[e.name]);
51033             });
51034             
51035             
51036         } else  if (this.tb.fields && this.tb.selectedNode) {
51037             this.tb.fields.each( function(e) {
51038                 if (e.stylename) {
51039                     e.setValue(this.tb.selectedNode.style[e.stylename]);
51040                     return;
51041                 } 
51042                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
51043             }, this);
51044             this.updateToolbarStyles(this.tb.selectedNode);  
51045         }
51046         
51047         
51048        
51049         Roo.menu.MenuMgr.hideAll();
51050
51051         
51052         
51053     
51054         // update the footer
51055         //
51056         this.updateFooter(ans);
51057              
51058     },
51059     
51060     updateToolbarStyles : function(sel)
51061     {
51062         var hasStyles = false;
51063         for(var i in this.styles) {
51064             hasStyles = true;
51065             break;
51066         }
51067         
51068         // update styles
51069         if (hasStyles && this.tb.hasStyles) { 
51070             var st = this.tb.fields.item(0);
51071             
51072             st.store.removeAll();
51073             var cn = sel.className.split(/\s+/);
51074             
51075             var avs = [];
51076             if (this.styles['*']) {
51077                 
51078                 Roo.each(this.styles['*'], function(v) {
51079                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
51080                 });
51081             }
51082             if (this.styles[tn]) { 
51083                 Roo.each(this.styles[tn], function(v) {
51084                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
51085                 });
51086             }
51087             
51088             st.store.loadData(avs);
51089             st.collapse();
51090             st.setValue(cn);
51091         }
51092     },
51093     
51094      
51095     updateFooter : function(ans)
51096     {
51097         var html = '';
51098         if (ans === false) {
51099             this.footDisp.dom.innerHTML = '';
51100             return;
51101         }
51102         
51103         this.footerEls = ans.reverse();
51104         Roo.each(this.footerEls, function(a,i) {
51105             if (!a) { return; }
51106             html += html.length ? ' &gt; '  :  '';
51107             
51108             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
51109             
51110         });
51111        
51112         // 
51113         var sz = this.footDisp.up('td').getSize();
51114         this.footDisp.dom.style.width = (sz.width -10) + 'px';
51115         this.footDisp.dom.style.marginLeft = '5px';
51116         
51117         this.footDisp.dom.style.overflow = 'hidden';
51118         
51119         this.footDisp.dom.innerHTML = html;
51120             
51121         
51122     },
51123    
51124        
51125     // private
51126     onDestroy : function(){
51127         if(this.rendered){
51128             
51129             this.tb.items.each(function(item){
51130                 if(item.menu){
51131                     item.menu.removeAll();
51132                     if(item.menu.el){
51133                         item.menu.el.destroy();
51134                     }
51135                 }
51136                 item.destroy();
51137             });
51138              
51139         }
51140     },
51141     onFirstFocus: function() {
51142         // need to do this for all the toolbars..
51143         this.tb.items.each(function(item){
51144            item.enable();
51145         });
51146     },
51147     buildToolbar: function(tlist, nm, friendly_name, block)
51148     {
51149         var editor = this.editor;
51150         var editorcore = this.editorcore;
51151          // create a new element.
51152         var wdiv = editor.wrap.createChild({
51153                 tag: 'div'
51154             }, editor.wrap.dom.firstChild.nextSibling, true);
51155         
51156        
51157         var tb = new Roo.Toolbar(wdiv);
51158         ///this.tb = tb; // << this sets the active toolbar..
51159         if (tlist === false && block) {
51160             tlist = block.contextMenu(this);
51161         }
51162         
51163         tb.hasStyles = false;
51164         tb.name = nm;
51165         
51166         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
51167         
51168         var styles = Array.from(this.styles);
51169         
51170         
51171         // styles...
51172         if (styles && styles.length) {
51173             tb.hasStyles = true;
51174             // this needs a multi-select checkbox...
51175             tb.addField( new Roo.form.ComboBox({
51176                 store: new Roo.data.SimpleStore({
51177                     id : 'val',
51178                     fields: ['val', 'selected'],
51179                     data : [] 
51180                 }),
51181                 name : '-roo-edit-className',
51182                 attrname : 'className',
51183                 displayField: 'val',
51184                 typeAhead: false,
51185                 mode: 'local',
51186                 editable : false,
51187                 triggerAction: 'all',
51188                 emptyText:'Select Style',
51189                 selectOnFocus:true,
51190                 width: 130,
51191                 listeners : {
51192                     'select': function(c, r, i) {
51193                         // initial support only for on class per el..
51194                         tb.selectedNode.className =  r ? r.get('val') : '';
51195                         editorcore.syncValue();
51196                     }
51197                 }
51198     
51199             }));
51200         }
51201         
51202         var tbc = Roo.form.HtmlEditor.ToolbarContext;
51203         
51204         
51205         for (var i = 0; i < tlist.length; i++) {
51206             
51207             // newer versions will use xtype cfg to create menus.
51208             if (typeof(tlist[i].xtype) != 'undefined') {
51209                 
51210                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
51211                 
51212                 
51213                 continue;
51214             }
51215             
51216             var item = tlist[i];
51217             tb.add(item.title + ":&nbsp;");
51218             
51219             
51220             //optname == used so you can configure the options available..
51221             var opts = item.opts ? item.opts : false;
51222             if (item.optname) { // use the b
51223                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
51224            
51225             }
51226             
51227             if (opts) {
51228                 // opts == pulldown..
51229                 tb.addField( new Roo.form.ComboBox({
51230                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
51231                         id : 'val',
51232                         fields: ['val', 'display'],
51233                         data : opts  
51234                     }),
51235                     name : '-roo-edit-' + tlist[i].name,
51236                     
51237                     attrname : tlist[i].name,
51238                     stylename : item.style ? item.style : false,
51239                     
51240                     displayField: item.displayField ? item.displayField : 'val',
51241                     valueField :  'val',
51242                     typeAhead: false,
51243                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
51244                     editable : false,
51245                     triggerAction: 'all',
51246                     emptyText:'Select',
51247                     selectOnFocus:true,
51248                     width: item.width ? item.width  : 130,
51249                     listeners : {
51250                         'select': function(c, r, i) {
51251                             if (tb.selectedNode.hasAttribute('data-block')) {
51252                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
51253                                 b[c.attrname] = r.get('val');
51254                                 b.updateElement(tb.selectedNode);
51255                                 editorcore.syncValue();
51256                                 return;
51257                             }
51258                             
51259                             if (c.stylename) {
51260                                 tb.selectedNode.style[c.stylename] =  r.get('val');
51261                                 editorcore.syncValue();
51262                                 return;
51263                             }
51264                             if (r === false) {
51265                                 tb.selectedNode.removeAttribute(c.attrname);
51266                                 editorcore.syncValue();
51267                                 return;
51268                             }
51269                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
51270                             editorcore.syncValue();
51271                         }
51272                     }
51273
51274                 }));
51275                 continue;
51276                     
51277                  
51278                 /*
51279                 tb.addField( new Roo.form.TextField({
51280                     name: i,
51281                     width: 100,
51282                     //allowBlank:false,
51283                     value: ''
51284                 }));
51285                 continue;
51286                 */
51287             }
51288             tb.addField( new Roo.form.TextField({
51289                 name: '-roo-edit-' + tlist[i].name,
51290                 attrname : tlist[i].name,
51291                 
51292                 width: item.width,
51293                 //allowBlank:true,
51294                 value: '',
51295                 listeners: {
51296                     'change' : function(f, nv, ov) {
51297                         
51298                         if (tb.selectedNode.hasAttribute('data-block')) {
51299                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
51300                             b[f.attrname] = nv;
51301                             b.updateElement(tb.selectedNode);
51302                             editorcore.syncValue();
51303                             return;
51304                         }
51305                         
51306                         tb.selectedNode.setAttribute(f.attrname, nv);
51307                         editorcore.syncValue();
51308                     }
51309                 }
51310             }));
51311              
51312         }
51313         
51314         var _this = this;
51315         
51316         if(nm == 'BODY'){
51317             tb.addSeparator();
51318         
51319             tb.addButton( {
51320                 text: 'Stylesheets',
51321
51322                 listeners : {
51323                     click : function ()
51324                     {
51325                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
51326                     }
51327                 }
51328             });
51329         }
51330         
51331         tb.addFill();
51332         tb.addButton({
51333             text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
51334     
51335             listeners : {
51336                 click : function ()
51337                 {
51338                     var sn = tb.selectedNode;
51339                     if (block) {
51340                         sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
51341                         
51342                     }
51343                     if (!sn) {
51344                         return;
51345                     }
51346                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
51347                     if (sn.hasAttribute('data-block')) {
51348                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
51349                         sn.parentNode.removeChild(sn);
51350                         
51351                     } else if (sn && sn.tagName != 'BODY') {
51352                         // remove and keep parents.
51353                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
51354                         a.removeTag(sn);
51355                     }
51356                     
51357                     
51358                     var range = editorcore.createRange();
51359         
51360                     range.setStart(stn,0);
51361                     range.setEnd(stn,0); 
51362                     var selection = editorcore.getSelection();
51363                     selection.removeAllRanges();
51364                     selection.addRange(range);
51365                     
51366                     
51367                     //_this.updateToolbar(null, null, pn);
51368                     _this.updateToolbar(null, null, null);
51369                     _this.updateFooter(false);
51370                     
51371                 }
51372             }
51373             
51374                     
51375                 
51376             
51377         });
51378         
51379         
51380         tb.el.on('click', function(e){
51381             e.preventDefault(); // what does this do?
51382         });
51383         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
51384         tb.el.hide();
51385         
51386         // dont need to disable them... as they will get hidden
51387         return tb;
51388          
51389         
51390     },
51391     buildFooter : function()
51392     {
51393         
51394         var fel = this.editor.wrap.createChild();
51395         this.footer = new Roo.Toolbar(fel);
51396         // toolbar has scrolly on left / right?
51397         var footDisp= new Roo.Toolbar.Fill();
51398         var _t = this;
51399         this.footer.add(
51400             {
51401                 text : '&lt;',
51402                 xtype: 'Button',
51403                 handler : function() {
51404                     _t.footDisp.scrollTo('left',0,true)
51405                 }
51406             }
51407         );
51408         this.footer.add( footDisp );
51409         this.footer.add( 
51410             {
51411                 text : '&gt;',
51412                 xtype: 'Button',
51413                 handler : function() {
51414                     // no animation..
51415                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
51416                 }
51417             }
51418         );
51419         var fel = Roo.get(footDisp.el);
51420         fel.addClass('x-editor-context');
51421         this.footDispWrap = fel; 
51422         this.footDispWrap.overflow  = 'hidden';
51423         
51424         this.footDisp = fel.createChild();
51425         this.footDispWrap.on('click', this.onContextClick, this)
51426         
51427         
51428     },
51429     // when the footer contect changes
51430     onContextClick : function (ev,dom)
51431     {
51432         ev.preventDefault();
51433         var  cn = dom.className;
51434         //Roo.log(cn);
51435         if (!cn.match(/x-ed-loc-/)) {
51436             return;
51437         }
51438         var n = cn.split('-').pop();
51439         var ans = this.footerEls;
51440         var sel = ans[n];
51441         
51442         this.editorcore.selectNode(sel);
51443         
51444         
51445         this.updateToolbar(null, null, sel);
51446         
51447         
51448     }
51449     
51450     
51451     
51452     
51453     
51454 });
51455
51456
51457
51458
51459
51460 /*
51461  * Based on:
51462  * Ext JS Library 1.1.1
51463  * Copyright(c) 2006-2007, Ext JS, LLC.
51464  *
51465  * Originally Released Under LGPL - original licence link has changed is not relivant.
51466  *
51467  * Fork - LGPL
51468  * <script type="text/javascript">
51469  */
51470  
51471 /**
51472  * @class Roo.form.BasicForm
51473  * @extends Roo.util.Observable
51474  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
51475  * @constructor
51476  * @param {String/HTMLElement/Roo.Element} el The form element or its id
51477  * @param {Object} config Configuration options
51478  */
51479 Roo.form.BasicForm = function(el, config){
51480     this.allItems = [];
51481     this.childForms = [];
51482     Roo.apply(this, config);
51483     /*
51484      * The Roo.form.Field items in this form.
51485      * @type MixedCollection
51486      */
51487      
51488      
51489     this.items = new Roo.util.MixedCollection(false, function(o){
51490         return o.id || (o.id = Roo.id());
51491     });
51492     this.addEvents({
51493         /**
51494          * @event beforeaction
51495          * Fires before any action is performed. Return false to cancel the action.
51496          * @param {Form} this
51497          * @param {Action} action The action to be performed
51498          */
51499         beforeaction: true,
51500         /**
51501          * @event actionfailed
51502          * Fires when an action fails.
51503          * @param {Form} this
51504          * @param {Action} action The action that failed
51505          */
51506         actionfailed : true,
51507         /**
51508          * @event actioncomplete
51509          * Fires when an action is completed.
51510          * @param {Form} this
51511          * @param {Action} action The action that completed
51512          */
51513         actioncomplete : true
51514     });
51515     if(el){
51516         this.initEl(el);
51517     }
51518     Roo.form.BasicForm.superclass.constructor.call(this);
51519     
51520     Roo.form.BasicForm.popover.apply();
51521 };
51522
51523 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
51524     /**
51525      * @cfg {String} method
51526      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
51527      */
51528     /**
51529      * @cfg {DataReader} reader
51530      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
51531      * This is optional as there is built-in support for processing JSON.
51532      */
51533     /**
51534      * @cfg {DataReader} errorReader
51535      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
51536      * This is completely optional as there is built-in support for processing JSON.
51537      */
51538     /**
51539      * @cfg {String} url
51540      * The URL to use for form actions if one isn't supplied in the action options.
51541      */
51542     /**
51543      * @cfg {Boolean} fileUpload
51544      * Set to true if this form is a file upload.
51545      */
51546      
51547     /**
51548      * @cfg {Object} baseParams
51549      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
51550      */
51551      /**
51552      
51553     /**
51554      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
51555      */
51556     timeout: 30,
51557
51558     // private
51559     activeAction : null,
51560
51561     /**
51562      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
51563      * or setValues() data instead of when the form was first created.
51564      */
51565     trackResetOnLoad : false,
51566     
51567     
51568     /**
51569      * childForms - used for multi-tab forms
51570      * @type {Array}
51571      */
51572     childForms : false,
51573     
51574     /**
51575      * allItems - full list of fields.
51576      * @type {Array}
51577      */
51578     allItems : false,
51579     
51580     /**
51581      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
51582      * element by passing it or its id or mask the form itself by passing in true.
51583      * @type Mixed
51584      */
51585     waitMsgTarget : false,
51586     
51587     /**
51588      * @type Boolean
51589      */
51590     disableMask : false,
51591     
51592     /**
51593      * @cfg {Boolean} errorMask (true|false) default false
51594      */
51595     errorMask : false,
51596     
51597     /**
51598      * @cfg {Number} maskOffset Default 100
51599      */
51600     maskOffset : 100,
51601
51602     // private
51603     initEl : function(el){
51604         this.el = Roo.get(el);
51605         this.id = this.el.id || Roo.id();
51606         this.el.on('submit', this.onSubmit, this);
51607         this.el.addClass('x-form');
51608     },
51609
51610     // private
51611     onSubmit : function(e){
51612         e.stopEvent();
51613     },
51614
51615     /**
51616      * Returns true if client-side validation on the form is successful.
51617      * @return Boolean
51618      */
51619     isValid : function(){
51620         var valid = true;
51621         var target = false;
51622         this.items.each(function(f){
51623             if(f.validate()){
51624                 return;
51625             }
51626             
51627             valid = false;
51628                 
51629             if(!target && f.el.isVisible(true)){
51630                 target = f;
51631             }
51632         });
51633         
51634         if(this.errorMask && !valid){
51635             Roo.form.BasicForm.popover.mask(this, target);
51636         }
51637         
51638         return valid;
51639     },
51640     /**
51641      * Returns array of invalid form fields.
51642      * @return Array
51643      */
51644     
51645     invalidFields : function()
51646     {
51647         var ret = [];
51648         this.items.each(function(f){
51649             if(f.validate()){
51650                 return;
51651             }
51652             ret.push(f);
51653             
51654         });
51655         
51656         return ret;
51657     },
51658     
51659     
51660     /**
51661      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
51662      * @return Boolean
51663      */
51664     isDirty : function(){
51665         var dirty = false;
51666         this.items.each(function(f){
51667            if(f.isDirty()){
51668                dirty = true;
51669                return false;
51670            }
51671         });
51672         return dirty;
51673     },
51674     
51675     /**
51676      * Returns true if any fields in this form have changed since their original load. (New version)
51677      * @return Boolean
51678      */
51679     
51680     hasChanged : function()
51681     {
51682         var dirty = false;
51683         this.items.each(function(f){
51684            if(f.hasChanged()){
51685                dirty = true;
51686                return false;
51687            }
51688         });
51689         return dirty;
51690         
51691     },
51692     /**
51693      * Resets all hasChanged to 'false' -
51694      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
51695      * So hasChanged storage is only to be used for this purpose
51696      * @return Boolean
51697      */
51698     resetHasChanged : function()
51699     {
51700         this.items.each(function(f){
51701            f.resetHasChanged();
51702         });
51703         
51704     },
51705     
51706     
51707     /**
51708      * Performs a predefined action (submit or load) or custom actions you define on this form.
51709      * @param {String} actionName The name of the action type
51710      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
51711      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
51712      * accept other config options):
51713      * <pre>
51714 Property          Type             Description
51715 ----------------  ---------------  ----------------------------------------------------------------------------------
51716 url               String           The url for the action (defaults to the form's url)
51717 method            String           The form method to use (defaults to the form's method, or POST if not defined)
51718 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
51719 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
51720                                    validate the form on the client (defaults to false)
51721      * </pre>
51722      * @return {BasicForm} this
51723      */
51724     doAction : function(action, options){
51725         if(typeof action == 'string'){
51726             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
51727         }
51728         if(this.fireEvent('beforeaction', this, action) !== false){
51729             this.beforeAction(action);
51730             action.run.defer(100, action);
51731         }
51732         return this;
51733     },
51734
51735     /**
51736      * Shortcut to do a submit action.
51737      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
51738      * @return {BasicForm} this
51739      */
51740     submit : function(options){
51741         this.doAction('submit', options);
51742         return this;
51743     },
51744
51745     /**
51746      * Shortcut to do a load action.
51747      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
51748      * @return {BasicForm} this
51749      */
51750     load : function(options){
51751         this.doAction('load', options);
51752         return this;
51753     },
51754
51755     /**
51756      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
51757      * @param {Record} record The record to edit
51758      * @return {BasicForm} this
51759      */
51760     updateRecord : function(record){
51761         record.beginEdit();
51762         var fs = record.fields;
51763         fs.each(function(f){
51764             var field = this.findField(f.name);
51765             if(field){
51766                 record.set(f.name, field.getValue());
51767             }
51768         }, this);
51769         record.endEdit();
51770         return this;
51771     },
51772
51773     /**
51774      * Loads an Roo.data.Record into this form.
51775      * @param {Record} record The record to load
51776      * @return {BasicForm} this
51777      */
51778     loadRecord : function(record){
51779         this.setValues(record.data);
51780         return this;
51781     },
51782
51783     // private
51784     beforeAction : function(action){
51785         var o = action.options;
51786         
51787         if(!this.disableMask) {
51788             if(this.waitMsgTarget === true){
51789                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
51790             }else if(this.waitMsgTarget){
51791                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
51792                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
51793             }else {
51794                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
51795             }
51796         }
51797         
51798          
51799     },
51800
51801     // private
51802     afterAction : function(action, success){
51803         this.activeAction = null;
51804         var o = action.options;
51805         
51806         if(!this.disableMask) {
51807             if(this.waitMsgTarget === true){
51808                 this.el.unmask();
51809             }else if(this.waitMsgTarget){
51810                 this.waitMsgTarget.unmask();
51811             }else{
51812                 Roo.MessageBox.updateProgress(1);
51813                 Roo.MessageBox.hide();
51814             }
51815         }
51816         
51817         if(success){
51818             if(o.reset){
51819                 this.reset();
51820             }
51821             Roo.callback(o.success, o.scope, [this, action]);
51822             this.fireEvent('actioncomplete', this, action);
51823             
51824         }else{
51825             
51826             // failure condition..
51827             // we have a scenario where updates need confirming.
51828             // eg. if a locking scenario exists..
51829             // we look for { errors : { needs_confirm : true }} in the response.
51830             if (
51831                 (typeof(action.result) != 'undefined')  &&
51832                 (typeof(action.result.errors) != 'undefined')  &&
51833                 (typeof(action.result.errors.needs_confirm) != 'undefined')
51834            ){
51835                 var _t = this;
51836                 Roo.MessageBox.confirm(
51837                     "Change requires confirmation",
51838                     action.result.errorMsg,
51839                     function(r) {
51840                         if (r != 'yes') {
51841                             return;
51842                         }
51843                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
51844                     }
51845                     
51846                 );
51847                 
51848                 
51849                 
51850                 return;
51851             }
51852             
51853             Roo.callback(o.failure, o.scope, [this, action]);
51854             // show an error message if no failed handler is set..
51855             if (!this.hasListener('actionfailed')) {
51856                 Roo.MessageBox.alert("Error",
51857                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
51858                         action.result.errorMsg :
51859                         "Saving Failed, please check your entries or try again"
51860                 );
51861             }
51862             
51863             this.fireEvent('actionfailed', this, action);
51864         }
51865         
51866     },
51867
51868     /**
51869      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
51870      * @param {String} id The value to search for
51871      * @return Field
51872      */
51873     findField : function(id){
51874         var field = this.items.get(id);
51875         if(!field){
51876             this.items.each(function(f){
51877                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
51878                     field = f;
51879                     return false;
51880                 }
51881             });
51882         }
51883         return field || null;
51884     },
51885
51886     /**
51887      * Add a secondary form to this one, 
51888      * Used to provide tabbed forms. One form is primary, with hidden values 
51889      * which mirror the elements from the other forms.
51890      * 
51891      * @param {Roo.form.Form} form to add.
51892      * 
51893      */
51894     addForm : function(form)
51895     {
51896        
51897         if (this.childForms.indexOf(form) > -1) {
51898             // already added..
51899             return;
51900         }
51901         this.childForms.push(form);
51902         var n = '';
51903         Roo.each(form.allItems, function (fe) {
51904             
51905             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
51906             if (this.findField(n)) { // already added..
51907                 return;
51908             }
51909             var add = new Roo.form.Hidden({
51910                 name : n
51911             });
51912             add.render(this.el);
51913             
51914             this.add( add );
51915         }, this);
51916         
51917     },
51918     /**
51919      * Mark fields in this form invalid in bulk.
51920      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
51921      * @return {BasicForm} this
51922      */
51923     markInvalid : function(errors){
51924         if(errors instanceof Array){
51925             for(var i = 0, len = errors.length; i < len; i++){
51926                 var fieldError = errors[i];
51927                 var f = this.findField(fieldError.id);
51928                 if(f){
51929                     f.markInvalid(fieldError.msg);
51930                 }
51931             }
51932         }else{
51933             var field, id;
51934             for(id in errors){
51935                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
51936                     field.markInvalid(errors[id]);
51937                 }
51938             }
51939         }
51940         Roo.each(this.childForms || [], function (f) {
51941             f.markInvalid(errors);
51942         });
51943         
51944         return this;
51945     },
51946
51947     /**
51948      * Set values for fields in this form in bulk.
51949      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
51950      * @return {BasicForm} this
51951      */
51952     setValues : function(values){
51953         if(values instanceof Array){ // array of objects
51954             for(var i = 0, len = values.length; i < len; i++){
51955                 var v = values[i];
51956                 var f = this.findField(v.id);
51957                 if(f){
51958                     f.setValue(v.value);
51959                     if(this.trackResetOnLoad){
51960                         f.originalValue = f.getValue();
51961                     }
51962                 }
51963             }
51964         }else{ // object hash
51965             var field, id;
51966             for(id in values){
51967                 if(typeof values[id] != 'function' && (field = this.findField(id))){
51968                     
51969                     if (field.setFromData && 
51970                         field.valueField && 
51971                         field.displayField &&
51972                         // combos' with local stores can 
51973                         // be queried via setValue()
51974                         // to set their value..
51975                         (field.store && !field.store.isLocal)
51976                         ) {
51977                         // it's a combo
51978                         var sd = { };
51979                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
51980                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
51981                         field.setFromData(sd);
51982                         
51983                     } else {
51984                         field.setValue(values[id]);
51985                     }
51986                     
51987                     
51988                     if(this.trackResetOnLoad){
51989                         field.originalValue = field.getValue();
51990                     }
51991                 }
51992             }
51993         }
51994         this.resetHasChanged();
51995         
51996         
51997         Roo.each(this.childForms || [], function (f) {
51998             f.setValues(values);
51999             f.resetHasChanged();
52000         });
52001                 
52002         return this;
52003     },
52004  
52005     /**
52006      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
52007      * they are returned as an array.
52008      * @param {Boolean} asString
52009      * @return {Object}
52010      */
52011     getValues : function(asString)
52012     {
52013         if (this.childForms) {
52014             // copy values from the child forms
52015             Roo.each(this.childForms, function (f) {
52016                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
52017             }, this);
52018         }
52019         
52020         // use formdata
52021         if (typeof(FormData) != 'undefined' && asString !== true) {
52022             // this relies on a 'recent' version of chrome apparently...
52023             try {
52024                 var fd = (new FormData(this.el.dom)).entries();
52025                 var ret = {};
52026                 var ent = fd.next();
52027                 while (!ent.done) {
52028                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
52029                     ent = fd.next();
52030                 };
52031                 return ret;
52032             } catch(e) {
52033                 
52034             }
52035             
52036         }
52037         
52038         
52039         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
52040         if(asString === true){
52041             return fs;
52042         }
52043         return Roo.urlDecode(fs);
52044     },
52045     
52046     /**
52047      * Returns the fields in this form as an object with key/value pairs. 
52048      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
52049      * Normally this will not return readOnly data 
52050      * @param {Boolean} with_readonly return readonly field data.
52051      * @return {Object}
52052      */
52053     getFieldValues : function(with_readonly)
52054     {
52055         if (this.childForms) {
52056             // copy values from the child forms
52057             // should this call getFieldValues - probably not as we do not currently copy
52058             // hidden fields when we generate..
52059             Roo.each(this.childForms, function (f) {
52060                 this.setValues(f.getFieldValues());
52061             }, this);
52062         }
52063         
52064         var ret = {};
52065         this.items.each(function(f){
52066             
52067             if (f.readOnly && with_readonly !== true) {
52068                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
52069                         // if a subform contains a copy of them.
52070                         // if you have subforms with the same editable data, you will need to copy the data back
52071                         // and forth.
52072             }
52073             
52074             if (!f.getName()) {
52075                 return;
52076             }
52077             var v = f.getValue();
52078             if (f.inputType =='radio') {
52079                 if (typeof(ret[f.getName()]) == 'undefined') {
52080                     ret[f.getName()] = ''; // empty..
52081                 }
52082                 
52083                 if (!f.el.dom.checked) {
52084                     return;
52085                     
52086                 }
52087                 v = f.el.dom.value;
52088                 
52089             }
52090             
52091             // not sure if this supported any more..
52092             if ((typeof(v) == 'object') && f.getRawValue) {
52093                 v = f.getRawValue() ; // dates..
52094             }
52095             // combo boxes where name != hiddenName...
52096             if (f.name != f.getName()) {
52097                 ret[f.name] = f.getRawValue();
52098             }
52099             ret[f.getName()] = v;
52100         });
52101         
52102         return ret;
52103     },
52104
52105     /**
52106      * Clears all invalid messages in this form.
52107      * @return {BasicForm} this
52108      */
52109     clearInvalid : function(){
52110         this.items.each(function(f){
52111            f.clearInvalid();
52112         });
52113         
52114         Roo.each(this.childForms || [], function (f) {
52115             f.clearInvalid();
52116         });
52117         
52118         
52119         return this;
52120     },
52121
52122     /**
52123      * Resets this form.
52124      * @return {BasicForm} this
52125      */
52126     reset : function(){
52127         this.items.each(function(f){
52128             f.reset();
52129         });
52130         
52131         Roo.each(this.childForms || [], function (f) {
52132             f.reset();
52133         });
52134         this.resetHasChanged();
52135         
52136         return this;
52137     },
52138
52139     /**
52140      * Add Roo.form components to this form.
52141      * @param {Field} field1
52142      * @param {Field} field2 (optional)
52143      * @param {Field} etc (optional)
52144      * @return {BasicForm} this
52145      */
52146     add : function(){
52147         this.items.addAll(Array.prototype.slice.call(arguments, 0));
52148         return this;
52149     },
52150
52151
52152     /**
52153      * Removes a field from the items collection (does NOT remove its markup).
52154      * @param {Field} field
52155      * @return {BasicForm} this
52156      */
52157     remove : function(field){
52158         this.items.remove(field);
52159         return this;
52160     },
52161
52162     /**
52163      * Looks at the fields in this form, checks them for an id attribute,
52164      * and calls applyTo on the existing dom element with that id.
52165      * @return {BasicForm} this
52166      */
52167     render : function(){
52168         this.items.each(function(f){
52169             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
52170                 f.applyTo(f.id);
52171             }
52172         });
52173         return this;
52174     },
52175
52176     /**
52177      * Calls {@link Ext#apply} for all fields in this form with the passed object.
52178      * @param {Object} values
52179      * @return {BasicForm} this
52180      */
52181     applyToFields : function(o){
52182         this.items.each(function(f){
52183            Roo.apply(f, o);
52184         });
52185         return this;
52186     },
52187
52188     /**
52189      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
52190      * @param {Object} values
52191      * @return {BasicForm} this
52192      */
52193     applyIfToFields : function(o){
52194         this.items.each(function(f){
52195            Roo.applyIf(f, o);
52196         });
52197         return this;
52198     }
52199 });
52200
52201 // back compat
52202 Roo.BasicForm = Roo.form.BasicForm;
52203
52204 Roo.apply(Roo.form.BasicForm, {
52205     
52206     popover : {
52207         
52208         padding : 5,
52209         
52210         isApplied : false,
52211         
52212         isMasked : false,
52213         
52214         form : false,
52215         
52216         target : false,
52217         
52218         intervalID : false,
52219         
52220         maskEl : false,
52221         
52222         apply : function()
52223         {
52224             if(this.isApplied){
52225                 return;
52226             }
52227             
52228             this.maskEl = {
52229                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
52230                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
52231                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
52232                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
52233             };
52234             
52235             this.maskEl.top.enableDisplayMode("block");
52236             this.maskEl.left.enableDisplayMode("block");
52237             this.maskEl.bottom.enableDisplayMode("block");
52238             this.maskEl.right.enableDisplayMode("block");
52239             
52240             Roo.get(document.body).on('click', function(){
52241                 this.unmask();
52242             }, this);
52243             
52244             Roo.get(document.body).on('touchstart', function(){
52245                 this.unmask();
52246             }, this);
52247             
52248             this.isApplied = true
52249         },
52250         
52251         mask : function(form, target)
52252         {
52253             this.form = form;
52254             
52255             this.target = target;
52256             
52257             if(!this.form.errorMask || !target.el){
52258                 return;
52259             }
52260             
52261             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
52262             
52263             var ot = this.target.el.calcOffsetsTo(scrollable);
52264             
52265             var scrollTo = ot[1] - this.form.maskOffset;
52266             
52267             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
52268             
52269             scrollable.scrollTo('top', scrollTo);
52270             
52271             var el = this.target.wrap || this.target.el;
52272             
52273             var box = el.getBox();
52274             
52275             this.maskEl.top.setStyle('position', 'absolute');
52276             this.maskEl.top.setStyle('z-index', 10000);
52277             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
52278             this.maskEl.top.setLeft(0);
52279             this.maskEl.top.setTop(0);
52280             this.maskEl.top.show();
52281             
52282             this.maskEl.left.setStyle('position', 'absolute');
52283             this.maskEl.left.setStyle('z-index', 10000);
52284             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
52285             this.maskEl.left.setLeft(0);
52286             this.maskEl.left.setTop(box.y - this.padding);
52287             this.maskEl.left.show();
52288
52289             this.maskEl.bottom.setStyle('position', 'absolute');
52290             this.maskEl.bottom.setStyle('z-index', 10000);
52291             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
52292             this.maskEl.bottom.setLeft(0);
52293             this.maskEl.bottom.setTop(box.bottom + this.padding);
52294             this.maskEl.bottom.show();
52295
52296             this.maskEl.right.setStyle('position', 'absolute');
52297             this.maskEl.right.setStyle('z-index', 10000);
52298             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
52299             this.maskEl.right.setLeft(box.right + this.padding);
52300             this.maskEl.right.setTop(box.y - this.padding);
52301             this.maskEl.right.show();
52302
52303             this.intervalID = window.setInterval(function() {
52304                 Roo.form.BasicForm.popover.unmask();
52305             }, 10000);
52306
52307             window.onwheel = function(){ return false;};
52308             
52309             (function(){ this.isMasked = true; }).defer(500, this);
52310             
52311         },
52312         
52313         unmask : function()
52314         {
52315             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
52316                 return;
52317             }
52318             
52319             this.maskEl.top.setStyle('position', 'absolute');
52320             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
52321             this.maskEl.top.hide();
52322
52323             this.maskEl.left.setStyle('position', 'absolute');
52324             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
52325             this.maskEl.left.hide();
52326
52327             this.maskEl.bottom.setStyle('position', 'absolute');
52328             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
52329             this.maskEl.bottom.hide();
52330
52331             this.maskEl.right.setStyle('position', 'absolute');
52332             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
52333             this.maskEl.right.hide();
52334             
52335             window.onwheel = function(){ return true;};
52336             
52337             if(this.intervalID){
52338                 window.clearInterval(this.intervalID);
52339                 this.intervalID = false;
52340             }
52341             
52342             this.isMasked = false;
52343             
52344         }
52345         
52346     }
52347     
52348 });/*
52349  * Based on:
52350  * Ext JS Library 1.1.1
52351  * Copyright(c) 2006-2007, Ext JS, LLC.
52352  *
52353  * Originally Released Under LGPL - original licence link has changed is not relivant.
52354  *
52355  * Fork - LGPL
52356  * <script type="text/javascript">
52357  */
52358
52359 /**
52360  * @class Roo.form.Form
52361  * @extends Roo.form.BasicForm
52362  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
52363  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
52364  * @constructor
52365  * @param {Object} config Configuration options
52366  */
52367 Roo.form.Form = function(config){
52368     var xitems =  [];
52369     if (config.items) {
52370         xitems = config.items;
52371         delete config.items;
52372     }
52373    
52374     
52375     Roo.form.Form.superclass.constructor.call(this, null, config);
52376     this.url = this.url || this.action;
52377     if(!this.root){
52378         this.root = new Roo.form.Layout(Roo.applyIf({
52379             id: Roo.id()
52380         }, config));
52381     }
52382     this.active = this.root;
52383     /**
52384      * Array of all the buttons that have been added to this form via {@link addButton}
52385      * @type Array
52386      */
52387     this.buttons = [];
52388     this.allItems = [];
52389     this.addEvents({
52390         /**
52391          * @event clientvalidation
52392          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
52393          * @param {Form} this
52394          * @param {Boolean} valid true if the form has passed client-side validation
52395          */
52396         clientvalidation: true,
52397         /**
52398          * @event rendered
52399          * Fires when the form is rendered
52400          * @param {Roo.form.Form} form
52401          */
52402         rendered : true
52403     });
52404     
52405     if (this.progressUrl) {
52406             // push a hidden field onto the list of fields..
52407             this.addxtype( {
52408                     xns: Roo.form, 
52409                     xtype : 'Hidden', 
52410                     name : 'UPLOAD_IDENTIFIER' 
52411             });
52412         }
52413         
52414     
52415     Roo.each(xitems, this.addxtype, this);
52416     
52417 };
52418
52419 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
52420      /**
52421      * @cfg {Roo.Button} buttons[] buttons at bottom of form
52422      */
52423     
52424     /**
52425      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
52426      */
52427     /**
52428      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
52429      */
52430     /**
52431      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
52432      */
52433     buttonAlign:'center',
52434
52435     /**
52436      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
52437      */
52438     minButtonWidth:75,
52439
52440     /**
52441      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
52442      * This property cascades to child containers if not set.
52443      */
52444     labelAlign:'left',
52445
52446     /**
52447      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
52448      * fires a looping event with that state. This is required to bind buttons to the valid
52449      * state using the config value formBind:true on the button.
52450      */
52451     monitorValid : false,
52452
52453     /**
52454      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
52455      */
52456     monitorPoll : 200,
52457     
52458     /**
52459      * @cfg {String} progressUrl - Url to return progress data 
52460      */
52461     
52462     progressUrl : false,
52463     /**
52464      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
52465      * sending a formdata with extra parameters - eg uploaded elements.
52466      */
52467     
52468     formData : false,
52469     
52470     /**
52471      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
52472      * fields are added and the column is closed. If no fields are passed the column remains open
52473      * until end() is called.
52474      * @param {Object} config The config to pass to the column
52475      * @param {Field} field1 (optional)
52476      * @param {Field} field2 (optional)
52477      * @param {Field} etc (optional)
52478      * @return Column The column container object
52479      */
52480     column : function(c){
52481         var col = new Roo.form.Column(c);
52482         this.start(col);
52483         if(arguments.length > 1){ // duplicate code required because of Opera
52484             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
52485             this.end();
52486         }
52487         return col;
52488     },
52489
52490     /**
52491      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
52492      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
52493      * until end() is called.
52494      * @param {Object} config The config to pass to the fieldset
52495      * @param {Field} field1 (optional)
52496      * @param {Field} field2 (optional)
52497      * @param {Field} etc (optional)
52498      * @return FieldSet The fieldset container object
52499      */
52500     fieldset : function(c){
52501         var fs = new Roo.form.FieldSet(c);
52502         this.start(fs);
52503         if(arguments.length > 1){ // duplicate code required because of Opera
52504             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
52505             this.end();
52506         }
52507         return fs;
52508     },
52509
52510     /**
52511      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
52512      * fields are added and the container is closed. If no fields are passed the container remains open
52513      * until end() is called.
52514      * @param {Object} config The config to pass to the Layout
52515      * @param {Field} field1 (optional)
52516      * @param {Field} field2 (optional)
52517      * @param {Field} etc (optional)
52518      * @return Layout The container object
52519      */
52520     container : function(c){
52521         var l = new Roo.form.Layout(c);
52522         this.start(l);
52523         if(arguments.length > 1){ // duplicate code required because of Opera
52524             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
52525             this.end();
52526         }
52527         return l;
52528     },
52529
52530     /**
52531      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
52532      * @param {Object} container A Roo.form.Layout or subclass of Layout
52533      * @return {Form} this
52534      */
52535     start : function(c){
52536         // cascade label info
52537         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
52538         this.active.stack.push(c);
52539         c.ownerCt = this.active;
52540         this.active = c;
52541         return this;
52542     },
52543
52544     /**
52545      * Closes the current open container
52546      * @return {Form} this
52547      */
52548     end : function(){
52549         if(this.active == this.root){
52550             return this;
52551         }
52552         this.active = this.active.ownerCt;
52553         return this;
52554     },
52555
52556     /**
52557      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
52558      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
52559      * as the label of the field.
52560      * @param {Field} field1
52561      * @param {Field} field2 (optional)
52562      * @param {Field} etc. (optional)
52563      * @return {Form} this
52564      */
52565     add : function(){
52566         this.active.stack.push.apply(this.active.stack, arguments);
52567         this.allItems.push.apply(this.allItems,arguments);
52568         var r = [];
52569         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
52570             if(a[i].isFormField){
52571                 r.push(a[i]);
52572             }
52573         }
52574         if(r.length > 0){
52575             Roo.form.Form.superclass.add.apply(this, r);
52576         }
52577         return this;
52578     },
52579     
52580
52581     
52582     
52583     
52584      /**
52585      * Find any element that has been added to a form, using it's ID or name
52586      * This can include framesets, columns etc. along with regular fields..
52587      * @param {String} id - id or name to find.
52588      
52589      * @return {Element} e - or false if nothing found.
52590      */
52591     findbyId : function(id)
52592     {
52593         var ret = false;
52594         if (!id) {
52595             return ret;
52596         }
52597         Roo.each(this.allItems, function(f){
52598             if (f.id == id || f.name == id ){
52599                 ret = f;
52600                 return false;
52601             }
52602         });
52603         return ret;
52604     },
52605
52606     
52607     
52608     /**
52609      * Render this form into the passed container. This should only be called once!
52610      * @param {String/HTMLElement/Element} container The element this component should be rendered into
52611      * @return {Form} this
52612      */
52613     render : function(ct)
52614     {
52615         
52616         
52617         
52618         ct = Roo.get(ct);
52619         var o = this.autoCreate || {
52620             tag: 'form',
52621             method : this.method || 'POST',
52622             id : this.id || Roo.id()
52623         };
52624         this.initEl(ct.createChild(o));
52625
52626         this.root.render(this.el);
52627         
52628        
52629              
52630         this.items.each(function(f){
52631             f.render('x-form-el-'+f.id);
52632         });
52633
52634         if(this.buttons.length > 0){
52635             // tables are required to maintain order and for correct IE layout
52636             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
52637                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
52638                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
52639             }}, null, true);
52640             var tr = tb.getElementsByTagName('tr')[0];
52641             for(var i = 0, len = this.buttons.length; i < len; i++) {
52642                 var b = this.buttons[i];
52643                 var td = document.createElement('td');
52644                 td.className = 'x-form-btn-td';
52645                 b.render(tr.appendChild(td));
52646             }
52647         }
52648         if(this.monitorValid){ // initialize after render
52649             this.startMonitoring();
52650         }
52651         this.fireEvent('rendered', this);
52652         return this;
52653     },
52654
52655     /**
52656      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
52657      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
52658      * object or a valid Roo.DomHelper element config
52659      * @param {Function} handler The function called when the button is clicked
52660      * @param {Object} scope (optional) The scope of the handler function
52661      * @return {Roo.Button}
52662      */
52663     addButton : function(config, handler, scope){
52664         var bc = {
52665             handler: handler,
52666             scope: scope,
52667             minWidth: this.minButtonWidth,
52668             hideParent:true
52669         };
52670         if(typeof config == "string"){
52671             bc.text = config;
52672         }else{
52673             Roo.apply(bc, config);
52674         }
52675         var btn = new Roo.Button(null, bc);
52676         this.buttons.push(btn);
52677         return btn;
52678     },
52679
52680      /**
52681      * Adds a series of form elements (using the xtype property as the factory method.
52682      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
52683      * @param {Object} config 
52684      */
52685     
52686     addxtype : function()
52687     {
52688         var ar = Array.prototype.slice.call(arguments, 0);
52689         var ret = false;
52690         for(var i = 0; i < ar.length; i++) {
52691             if (!ar[i]) {
52692                 continue; // skip -- if this happends something invalid got sent, we 
52693                 // should ignore it, as basically that interface element will not show up
52694                 // and that should be pretty obvious!!
52695             }
52696             
52697             if (Roo.form[ar[i].xtype]) {
52698                 ar[i].form = this;
52699                 var fe = Roo.factory(ar[i], Roo.form);
52700                 if (!ret) {
52701                     ret = fe;
52702                 }
52703                 fe.form = this;
52704                 if (fe.store) {
52705                     fe.store.form = this;
52706                 }
52707                 if (fe.isLayout) {  
52708                          
52709                     this.start(fe);
52710                     this.allItems.push(fe);
52711                     if (fe.items && fe.addxtype) {
52712                         fe.addxtype.apply(fe, fe.items);
52713                         delete fe.items;
52714                     }
52715                      this.end();
52716                     continue;
52717                 }
52718                 
52719                 
52720                  
52721                 this.add(fe);
52722               //  console.log('adding ' + ar[i].xtype);
52723             }
52724             if (ar[i].xtype == 'Button') {  
52725                 //console.log('adding button');
52726                 //console.log(ar[i]);
52727                 this.addButton(ar[i]);
52728                 this.allItems.push(fe);
52729                 continue;
52730             }
52731             
52732             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
52733                 alert('end is not supported on xtype any more, use items');
52734             //    this.end();
52735             //    //console.log('adding end');
52736             }
52737             
52738         }
52739         return ret;
52740     },
52741     
52742     /**
52743      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
52744      * option "monitorValid"
52745      */
52746     startMonitoring : function(){
52747         if(!this.bound){
52748             this.bound = true;
52749             Roo.TaskMgr.start({
52750                 run : this.bindHandler,
52751                 interval : this.monitorPoll || 200,
52752                 scope: this
52753             });
52754         }
52755     },
52756
52757     /**
52758      * Stops monitoring of the valid state of this form
52759      */
52760     stopMonitoring : function(){
52761         this.bound = false;
52762     },
52763
52764     // private
52765     bindHandler : function(){
52766         if(!this.bound){
52767             return false; // stops binding
52768         }
52769         var valid = true;
52770         this.items.each(function(f){
52771             if(!f.isValid(true)){
52772                 valid = false;
52773                 return false;
52774             }
52775         });
52776         for(var i = 0, len = this.buttons.length; i < len; i++){
52777             var btn = this.buttons[i];
52778             if(btn.formBind === true && btn.disabled === valid){
52779                 btn.setDisabled(!valid);
52780             }
52781         }
52782         this.fireEvent('clientvalidation', this, valid);
52783     }
52784     
52785     
52786     
52787     
52788     
52789     
52790     
52791     
52792 });
52793
52794
52795 // back compat
52796 Roo.Form = Roo.form.Form;
52797 /*
52798  * Based on:
52799  * Ext JS Library 1.1.1
52800  * Copyright(c) 2006-2007, Ext JS, LLC.
52801  *
52802  * Originally Released Under LGPL - original licence link has changed is not relivant.
52803  *
52804  * Fork - LGPL
52805  * <script type="text/javascript">
52806  */
52807
52808 // as we use this in bootstrap.
52809 Roo.namespace('Roo.form');
52810  /**
52811  * @class Roo.form.Action
52812  * Internal Class used to handle form actions
52813  * @constructor
52814  * @param {Roo.form.BasicForm} el The form element or its id
52815  * @param {Object} config Configuration options
52816  */
52817
52818  
52819  
52820 // define the action interface
52821 Roo.form.Action = function(form, options){
52822     this.form = form;
52823     this.options = options || {};
52824 };
52825 /**
52826  * Client Validation Failed
52827  * @const 
52828  */
52829 Roo.form.Action.CLIENT_INVALID = 'client';
52830 /**
52831  * Server Validation Failed
52832  * @const 
52833  */
52834 Roo.form.Action.SERVER_INVALID = 'server';
52835  /**
52836  * Connect to Server Failed
52837  * @const 
52838  */
52839 Roo.form.Action.CONNECT_FAILURE = 'connect';
52840 /**
52841  * Reading Data from Server Failed
52842  * @const 
52843  */
52844 Roo.form.Action.LOAD_FAILURE = 'load';
52845
52846 Roo.form.Action.prototype = {
52847     type : 'default',
52848     failureType : undefined,
52849     response : undefined,
52850     result : undefined,
52851
52852     // interface method
52853     run : function(options){
52854
52855     },
52856
52857     // interface method
52858     success : function(response){
52859
52860     },
52861
52862     // interface method
52863     handleResponse : function(response){
52864
52865     },
52866
52867     // default connection failure
52868     failure : function(response){
52869         
52870         this.response = response;
52871         this.failureType = Roo.form.Action.CONNECT_FAILURE;
52872         this.form.afterAction(this, false);
52873     },
52874
52875     processResponse : function(response){
52876         this.response = response;
52877         if(!response.responseText){
52878             return true;
52879         }
52880         this.result = this.handleResponse(response);
52881         return this.result;
52882     },
52883
52884     // utility functions used internally
52885     getUrl : function(appendParams){
52886         var url = this.options.url || this.form.url || this.form.el.dom.action;
52887         if(appendParams){
52888             var p = this.getParams();
52889             if(p){
52890                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
52891             }
52892         }
52893         return url;
52894     },
52895
52896     getMethod : function(){
52897         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
52898     },
52899
52900     getParams : function(){
52901         var bp = this.form.baseParams;
52902         var p = this.options.params;
52903         if(p){
52904             if(typeof p == "object"){
52905                 p = Roo.urlEncode(Roo.applyIf(p, bp));
52906             }else if(typeof p == 'string' && bp){
52907                 p += '&' + Roo.urlEncode(bp);
52908             }
52909         }else if(bp){
52910             p = Roo.urlEncode(bp);
52911         }
52912         return p;
52913     },
52914
52915     createCallback : function(){
52916         return {
52917             success: this.success,
52918             failure: this.failure,
52919             scope: this,
52920             timeout: (this.form.timeout*1000),
52921             upload: this.form.fileUpload ? this.success : undefined
52922         };
52923     }
52924 };
52925
52926 Roo.form.Action.Submit = function(form, options){
52927     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
52928 };
52929
52930 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
52931     type : 'submit',
52932
52933     haveProgress : false,
52934     uploadComplete : false,
52935     
52936     // uploadProgress indicator.
52937     uploadProgress : function()
52938     {
52939         if (!this.form.progressUrl) {
52940             return;
52941         }
52942         
52943         if (!this.haveProgress) {
52944             Roo.MessageBox.progress("Uploading", "Uploading");
52945         }
52946         if (this.uploadComplete) {
52947            Roo.MessageBox.hide();
52948            return;
52949         }
52950         
52951         this.haveProgress = true;
52952    
52953         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
52954         
52955         var c = new Roo.data.Connection();
52956         c.request({
52957             url : this.form.progressUrl,
52958             params: {
52959                 id : uid
52960             },
52961             method: 'GET',
52962             success : function(req){
52963                //console.log(data);
52964                 var rdata = false;
52965                 var edata;
52966                 try  {
52967                    rdata = Roo.decode(req.responseText)
52968                 } catch (e) {
52969                     Roo.log("Invalid data from server..");
52970                     Roo.log(edata);
52971                     return;
52972                 }
52973                 if (!rdata || !rdata.success) {
52974                     Roo.log(rdata);
52975                     Roo.MessageBox.alert(Roo.encode(rdata));
52976                     return;
52977                 }
52978                 var data = rdata.data;
52979                 
52980                 if (this.uploadComplete) {
52981                    Roo.MessageBox.hide();
52982                    return;
52983                 }
52984                    
52985                 if (data){
52986                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
52987                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
52988                     );
52989                 }
52990                 this.uploadProgress.defer(2000,this);
52991             },
52992        
52993             failure: function(data) {
52994                 Roo.log('progress url failed ');
52995                 Roo.log(data);
52996             },
52997             scope : this
52998         });
52999            
53000     },
53001     
53002     
53003     run : function()
53004     {
53005         // run get Values on the form, so it syncs any secondary forms.
53006         this.form.getValues();
53007         
53008         var o = this.options;
53009         var method = this.getMethod();
53010         var isPost = method == 'POST';
53011         if(o.clientValidation === false || this.form.isValid()){
53012             
53013             if (this.form.progressUrl) {
53014                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
53015                     (new Date() * 1) + '' + Math.random());
53016                     
53017             } 
53018             
53019             
53020             Roo.Ajax.request(Roo.apply(this.createCallback(), {
53021                 form:this.form.el.dom,
53022                 url:this.getUrl(!isPost),
53023                 method: method,
53024                 params:isPost ? this.getParams() : null,
53025                 isUpload: this.form.fileUpload,
53026                 formData : this.form.formData
53027             }));
53028             
53029             this.uploadProgress();
53030
53031         }else if (o.clientValidation !== false){ // client validation failed
53032             this.failureType = Roo.form.Action.CLIENT_INVALID;
53033             this.form.afterAction(this, false);
53034         }
53035     },
53036
53037     success : function(response)
53038     {
53039         this.uploadComplete= true;
53040         if (this.haveProgress) {
53041             Roo.MessageBox.hide();
53042         }
53043         
53044         
53045         var result = this.processResponse(response);
53046         if(result === true || result.success){
53047             this.form.afterAction(this, true);
53048             return;
53049         }
53050         if(result.errors){
53051             this.form.markInvalid(result.errors);
53052             this.failureType = Roo.form.Action.SERVER_INVALID;
53053         }
53054         this.form.afterAction(this, false);
53055     },
53056     failure : function(response)
53057     {
53058         this.uploadComplete= true;
53059         if (this.haveProgress) {
53060             Roo.MessageBox.hide();
53061         }
53062         
53063         this.response = response;
53064         this.failureType = Roo.form.Action.CONNECT_FAILURE;
53065         this.form.afterAction(this, false);
53066     },
53067     
53068     handleResponse : function(response){
53069         if(this.form.errorReader){
53070             var rs = this.form.errorReader.read(response);
53071             var errors = [];
53072             if(rs.records){
53073                 for(var i = 0, len = rs.records.length; i < len; i++) {
53074                     var r = rs.records[i];
53075                     errors[i] = r.data;
53076                 }
53077             }
53078             if(errors.length < 1){
53079                 errors = null;
53080             }
53081             return {
53082                 success : rs.success,
53083                 errors : errors
53084             };
53085         }
53086         var ret = false;
53087         try {
53088             ret = Roo.decode(response.responseText);
53089         } catch (e) {
53090             ret = {
53091                 success: false,
53092                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
53093                 errors : []
53094             };
53095         }
53096         return ret;
53097         
53098     }
53099 });
53100
53101
53102 Roo.form.Action.Load = function(form, options){
53103     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
53104     this.reader = this.form.reader;
53105 };
53106
53107 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
53108     type : 'load',
53109
53110     run : function(){
53111         
53112         Roo.Ajax.request(Roo.apply(
53113                 this.createCallback(), {
53114                     method:this.getMethod(),
53115                     url:this.getUrl(false),
53116                     params:this.getParams()
53117         }));
53118     },
53119
53120     success : function(response){
53121         
53122         var result = this.processResponse(response);
53123         if(result === true || !result.success || !result.data){
53124             this.failureType = Roo.form.Action.LOAD_FAILURE;
53125             this.form.afterAction(this, false);
53126             return;
53127         }
53128         this.form.clearInvalid();
53129         this.form.setValues(result.data);
53130         this.form.afterAction(this, true);
53131     },
53132
53133     handleResponse : function(response){
53134         if(this.form.reader){
53135             var rs = this.form.reader.read(response);
53136             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
53137             return {
53138                 success : rs.success,
53139                 data : data
53140             };
53141         }
53142         return Roo.decode(response.responseText);
53143     }
53144 });
53145
53146 Roo.form.Action.ACTION_TYPES = {
53147     'load' : Roo.form.Action.Load,
53148     'submit' : Roo.form.Action.Submit
53149 };/*
53150  * Based on:
53151  * Ext JS Library 1.1.1
53152  * Copyright(c) 2006-2007, Ext JS, LLC.
53153  *
53154  * Originally Released Under LGPL - original licence link has changed is not relivant.
53155  *
53156  * Fork - LGPL
53157  * <script type="text/javascript">
53158  */
53159  
53160 /**
53161  * @class Roo.form.Layout
53162  * @extends Roo.Component
53163  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
53164  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
53165  * @constructor
53166  * @param {Object} config Configuration options
53167  */
53168 Roo.form.Layout = function(config){
53169     var xitems = [];
53170     if (config.items) {
53171         xitems = config.items;
53172         delete config.items;
53173     }
53174     Roo.form.Layout.superclass.constructor.call(this, config);
53175     this.stack = [];
53176     Roo.each(xitems, this.addxtype, this);
53177      
53178 };
53179
53180 Roo.extend(Roo.form.Layout, Roo.Component, {
53181     /**
53182      * @cfg {String/Object} autoCreate
53183      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
53184      */
53185     /**
53186      * @cfg {String/Object/Function} style
53187      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
53188      * a function which returns such a specification.
53189      */
53190     /**
53191      * @cfg {String} labelAlign
53192      * Valid values are "left," "top" and "right" (defaults to "left")
53193      */
53194     /**
53195      * @cfg {Number} labelWidth
53196      * Fixed width in pixels of all field labels (defaults to undefined)
53197      */
53198     /**
53199      * @cfg {Boolean} clear
53200      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
53201      */
53202     clear : true,
53203     /**
53204      * @cfg {String} labelSeparator
53205      * The separator to use after field labels (defaults to ':')
53206      */
53207     labelSeparator : ':',
53208     /**
53209      * @cfg {Boolean} hideLabels
53210      * True to suppress the display of field labels in this layout (defaults to false)
53211      */
53212     hideLabels : false,
53213
53214     // private
53215     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
53216     
53217     isLayout : true,
53218     
53219     // private
53220     onRender : function(ct, position){
53221         if(this.el){ // from markup
53222             this.el = Roo.get(this.el);
53223         }else {  // generate
53224             var cfg = this.getAutoCreate();
53225             this.el = ct.createChild(cfg, position);
53226         }
53227         if(this.style){
53228             this.el.applyStyles(this.style);
53229         }
53230         if(this.labelAlign){
53231             this.el.addClass('x-form-label-'+this.labelAlign);
53232         }
53233         if(this.hideLabels){
53234             this.labelStyle = "display:none";
53235             this.elementStyle = "padding-left:0;";
53236         }else{
53237             if(typeof this.labelWidth == 'number'){
53238                 this.labelStyle = "width:"+this.labelWidth+"px;";
53239                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
53240             }
53241             if(this.labelAlign == 'top'){
53242                 this.labelStyle = "width:auto;";
53243                 this.elementStyle = "padding-left:0;";
53244             }
53245         }
53246         var stack = this.stack;
53247         var slen = stack.length;
53248         if(slen > 0){
53249             if(!this.fieldTpl){
53250                 var t = new Roo.Template(
53251                     '<div class="x-form-item {5}">',
53252                         '<label for="{0}" style="{2}">{1}{4}</label>',
53253                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
53254                         '</div>',
53255                     '</div><div class="x-form-clear-left"></div>'
53256                 );
53257                 t.disableFormats = true;
53258                 t.compile();
53259                 Roo.form.Layout.prototype.fieldTpl = t;
53260             }
53261             for(var i = 0; i < slen; i++) {
53262                 if(stack[i].isFormField){
53263                     this.renderField(stack[i]);
53264                 }else{
53265                     this.renderComponent(stack[i]);
53266                 }
53267             }
53268         }
53269         if(this.clear){
53270             this.el.createChild({cls:'x-form-clear'});
53271         }
53272     },
53273
53274     // private
53275     renderField : function(f){
53276         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
53277                f.id, //0
53278                f.fieldLabel, //1
53279                f.labelStyle||this.labelStyle||'', //2
53280                this.elementStyle||'', //3
53281                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
53282                f.itemCls||this.itemCls||''  //5
53283        ], true).getPrevSibling());
53284     },
53285
53286     // private
53287     renderComponent : function(c){
53288         c.render(c.isLayout ? this.el : this.el.createChild());    
53289     },
53290     /**
53291      * Adds a object form elements (using the xtype property as the factory method.)
53292      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
53293      * @param {Object} config 
53294      */
53295     addxtype : function(o)
53296     {
53297         // create the lement.
53298         o.form = this.form;
53299         var fe = Roo.factory(o, Roo.form);
53300         this.form.allItems.push(fe);
53301         this.stack.push(fe);
53302         
53303         if (fe.isFormField) {
53304             this.form.items.add(fe);
53305         }
53306          
53307         return fe;
53308     }
53309 });
53310
53311 /**
53312  * @class Roo.form.Column
53313  * @extends Roo.form.Layout
53314  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
53315  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
53316  * @constructor
53317  * @param {Object} config Configuration options
53318  */
53319 Roo.form.Column = function(config){
53320     Roo.form.Column.superclass.constructor.call(this, config);
53321 };
53322
53323 Roo.extend(Roo.form.Column, Roo.form.Layout, {
53324     /**
53325      * @cfg {Number/String} width
53326      * The fixed width of the column in pixels or CSS value (defaults to "auto")
53327      */
53328     /**
53329      * @cfg {String/Object} autoCreate
53330      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
53331      */
53332
53333     // private
53334     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
53335
53336     // private
53337     onRender : function(ct, position){
53338         Roo.form.Column.superclass.onRender.call(this, ct, position);
53339         if(this.width){
53340             this.el.setWidth(this.width);
53341         }
53342     }
53343 });
53344
53345
53346 /**
53347  * @class Roo.form.Row
53348  * @extends Roo.form.Layout
53349  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
53350  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
53351  * @constructor
53352  * @param {Object} config Configuration options
53353  */
53354
53355  
53356 Roo.form.Row = function(config){
53357     Roo.form.Row.superclass.constructor.call(this, config);
53358 };
53359  
53360 Roo.extend(Roo.form.Row, Roo.form.Layout, {
53361       /**
53362      * @cfg {Number/String} width
53363      * The fixed width of the column in pixels or CSS value (defaults to "auto")
53364      */
53365     /**
53366      * @cfg {Number/String} height
53367      * The fixed height of the column in pixels or CSS value (defaults to "auto")
53368      */
53369     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
53370     
53371     padWidth : 20,
53372     // private
53373     onRender : function(ct, position){
53374         //console.log('row render');
53375         if(!this.rowTpl){
53376             var t = new Roo.Template(
53377                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
53378                     '<label for="{0}" style="{2}">{1}{4}</label>',
53379                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
53380                     '</div>',
53381                 '</div>'
53382             );
53383             t.disableFormats = true;
53384             t.compile();
53385             Roo.form.Layout.prototype.rowTpl = t;
53386         }
53387         this.fieldTpl = this.rowTpl;
53388         
53389         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
53390         var labelWidth = 100;
53391         
53392         if ((this.labelAlign != 'top')) {
53393             if (typeof this.labelWidth == 'number') {
53394                 labelWidth = this.labelWidth
53395             }
53396             this.padWidth =  20 + labelWidth;
53397             
53398         }
53399         
53400         Roo.form.Column.superclass.onRender.call(this, ct, position);
53401         if(this.width){
53402             this.el.setWidth(this.width);
53403         }
53404         if(this.height){
53405             this.el.setHeight(this.height);
53406         }
53407     },
53408     
53409     // private
53410     renderField : function(f){
53411         f.fieldEl = this.fieldTpl.append(this.el, [
53412                f.id, f.fieldLabel,
53413                f.labelStyle||this.labelStyle||'',
53414                this.elementStyle||'',
53415                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
53416                f.itemCls||this.itemCls||'',
53417                f.width ? f.width + this.padWidth : 160 + this.padWidth
53418        ],true);
53419     }
53420 });
53421  
53422
53423 /**
53424  * @class Roo.form.FieldSet
53425  * @extends Roo.form.Layout
53426  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
53427  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
53428  * @constructor
53429  * @param {Object} config Configuration options
53430  */
53431 Roo.form.FieldSet = function(config){
53432     Roo.form.FieldSet.superclass.constructor.call(this, config);
53433 };
53434
53435 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
53436     /**
53437      * @cfg {String} legend
53438      * The text to display as the legend for the FieldSet (defaults to '')
53439      */
53440     /**
53441      * @cfg {String/Object} autoCreate
53442      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
53443      */
53444
53445     // private
53446     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
53447
53448     // private
53449     onRender : function(ct, position){
53450         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
53451         if(this.legend){
53452             this.setLegend(this.legend);
53453         }
53454     },
53455
53456     // private
53457     setLegend : function(text){
53458         if(this.rendered){
53459             this.el.child('legend').update(text);
53460         }
53461     }
53462 });/*
53463  * Based on:
53464  * Ext JS Library 1.1.1
53465  * Copyright(c) 2006-2007, Ext JS, LLC.
53466  *
53467  * Originally Released Under LGPL - original licence link has changed is not relivant.
53468  *
53469  * Fork - LGPL
53470  * <script type="text/javascript">
53471  */
53472 /**
53473  * @class Roo.form.VTypes
53474  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
53475  * @static
53476  */
53477 Roo.form.VTypes = function(){
53478     // closure these in so they are only created once.
53479     var alpha = /^[a-zA-Z_]+$/;
53480     var alphanum = /^[a-zA-Z0-9_]+$/;
53481     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
53482     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
53483
53484     // All these messages and functions are configurable
53485     return {
53486         /**
53487          * The function used to validate email addresses
53488          * @param {String} value The email address
53489          */
53490         'email' : function(v){
53491             return email.test(v);
53492         },
53493         /**
53494          * The error text to display when the email validation function returns false
53495          * @type String
53496          */
53497         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
53498         /**
53499          * The keystroke filter mask to be applied on email input
53500          * @type RegExp
53501          */
53502         'emailMask' : /[a-z0-9_\.\-@]/i,
53503
53504         /**
53505          * The function used to validate URLs
53506          * @param {String} value The URL
53507          */
53508         'url' : function(v){
53509             return url.test(v);
53510         },
53511         /**
53512          * The error text to display when the url validation function returns false
53513          * @type String
53514          */
53515         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
53516         
53517         /**
53518          * The function used to validate alpha values
53519          * @param {String} value The value
53520          */
53521         'alpha' : function(v){
53522             return alpha.test(v);
53523         },
53524         /**
53525          * The error text to display when the alpha validation function returns false
53526          * @type String
53527          */
53528         'alphaText' : 'This field should only contain letters and _',
53529         /**
53530          * The keystroke filter mask to be applied on alpha input
53531          * @type RegExp
53532          */
53533         'alphaMask' : /[a-z_]/i,
53534
53535         /**
53536          * The function used to validate alphanumeric values
53537          * @param {String} value The value
53538          */
53539         'alphanum' : function(v){
53540             return alphanum.test(v);
53541         },
53542         /**
53543          * The error text to display when the alphanumeric validation function returns false
53544          * @type String
53545          */
53546         'alphanumText' : 'This field should only contain letters, numbers and _',
53547         /**
53548          * The keystroke filter mask to be applied on alphanumeric input
53549          * @type RegExp
53550          */
53551         'alphanumMask' : /[a-z0-9_]/i
53552     };
53553 }();//<script type="text/javascript">
53554
53555 /**
53556  * @class Roo.form.FCKeditor
53557  * @extends Roo.form.TextArea
53558  * Wrapper around the FCKEditor http://www.fckeditor.net
53559  * @constructor
53560  * Creates a new FCKeditor
53561  * @param {Object} config Configuration options
53562  */
53563 Roo.form.FCKeditor = function(config){
53564     Roo.form.FCKeditor.superclass.constructor.call(this, config);
53565     this.addEvents({
53566          /**
53567          * @event editorinit
53568          * Fired when the editor is initialized - you can add extra handlers here..
53569          * @param {FCKeditor} this
53570          * @param {Object} the FCK object.
53571          */
53572         editorinit : true
53573     });
53574     
53575     
53576 };
53577 Roo.form.FCKeditor.editors = { };
53578 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
53579 {
53580     //defaultAutoCreate : {
53581     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
53582     //},
53583     // private
53584     /**
53585      * @cfg {Object} fck options - see fck manual for details.
53586      */
53587     fckconfig : false,
53588     
53589     /**
53590      * @cfg {Object} fck toolbar set (Basic or Default)
53591      */
53592     toolbarSet : 'Basic',
53593     /**
53594      * @cfg {Object} fck BasePath
53595      */ 
53596     basePath : '/fckeditor/',
53597     
53598     
53599     frame : false,
53600     
53601     value : '',
53602     
53603    
53604     onRender : function(ct, position)
53605     {
53606         if(!this.el){
53607             this.defaultAutoCreate = {
53608                 tag: "textarea",
53609                 style:"width:300px;height:60px;",
53610                 autocomplete: "new-password"
53611             };
53612         }
53613         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
53614         /*
53615         if(this.grow){
53616             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
53617             if(this.preventScrollbars){
53618                 this.el.setStyle("overflow", "hidden");
53619             }
53620             this.el.setHeight(this.growMin);
53621         }
53622         */
53623         //console.log('onrender' + this.getId() );
53624         Roo.form.FCKeditor.editors[this.getId()] = this;
53625          
53626
53627         this.replaceTextarea() ;
53628         
53629     },
53630     
53631     getEditor : function() {
53632         return this.fckEditor;
53633     },
53634     /**
53635      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
53636      * @param {Mixed} value The value to set
53637      */
53638     
53639     
53640     setValue : function(value)
53641     {
53642         //console.log('setValue: ' + value);
53643         
53644         if(typeof(value) == 'undefined') { // not sure why this is happending...
53645             return;
53646         }
53647         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
53648         
53649         //if(!this.el || !this.getEditor()) {
53650         //    this.value = value;
53651             //this.setValue.defer(100,this,[value]);    
53652         //    return;
53653         //} 
53654         
53655         if(!this.getEditor()) {
53656             return;
53657         }
53658         
53659         this.getEditor().SetData(value);
53660         
53661         //
53662
53663     },
53664
53665     /**
53666      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
53667      * @return {Mixed} value The field value
53668      */
53669     getValue : function()
53670     {
53671         
53672         if (this.frame && this.frame.dom.style.display == 'none') {
53673             return Roo.form.FCKeditor.superclass.getValue.call(this);
53674         }
53675         
53676         if(!this.el || !this.getEditor()) {
53677            
53678            // this.getValue.defer(100,this); 
53679             return this.value;
53680         }
53681        
53682         
53683         var value=this.getEditor().GetData();
53684         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
53685         return Roo.form.FCKeditor.superclass.getValue.call(this);
53686         
53687
53688     },
53689
53690     /**
53691      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
53692      * @return {Mixed} value The field value
53693      */
53694     getRawValue : function()
53695     {
53696         if (this.frame && this.frame.dom.style.display == 'none') {
53697             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
53698         }
53699         
53700         if(!this.el || !this.getEditor()) {
53701             //this.getRawValue.defer(100,this); 
53702             return this.value;
53703             return;
53704         }
53705         
53706         
53707         
53708         var value=this.getEditor().GetData();
53709         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
53710         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
53711          
53712     },
53713     
53714     setSize : function(w,h) {
53715         
53716         
53717         
53718         //if (this.frame && this.frame.dom.style.display == 'none') {
53719         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
53720         //    return;
53721         //}
53722         //if(!this.el || !this.getEditor()) {
53723         //    this.setSize.defer(100,this, [w,h]); 
53724         //    return;
53725         //}
53726         
53727         
53728         
53729         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
53730         
53731         this.frame.dom.setAttribute('width', w);
53732         this.frame.dom.setAttribute('height', h);
53733         this.frame.setSize(w,h);
53734         
53735     },
53736     
53737     toggleSourceEdit : function(value) {
53738         
53739       
53740          
53741         this.el.dom.style.display = value ? '' : 'none';
53742         this.frame.dom.style.display = value ?  'none' : '';
53743         
53744     },
53745     
53746     
53747     focus: function(tag)
53748     {
53749         if (this.frame.dom.style.display == 'none') {
53750             return Roo.form.FCKeditor.superclass.focus.call(this);
53751         }
53752         if(!this.el || !this.getEditor()) {
53753             this.focus.defer(100,this, [tag]); 
53754             return;
53755         }
53756         
53757         
53758         
53759         
53760         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
53761         this.getEditor().Focus();
53762         if (tgs.length) {
53763             if (!this.getEditor().Selection.GetSelection()) {
53764                 this.focus.defer(100,this, [tag]); 
53765                 return;
53766             }
53767             
53768             
53769             var r = this.getEditor().EditorDocument.createRange();
53770             r.setStart(tgs[0],0);
53771             r.setEnd(tgs[0],0);
53772             this.getEditor().Selection.GetSelection().removeAllRanges();
53773             this.getEditor().Selection.GetSelection().addRange(r);
53774             this.getEditor().Focus();
53775         }
53776         
53777     },
53778     
53779     
53780     
53781     replaceTextarea : function()
53782     {
53783         if ( document.getElementById( this.getId() + '___Frame' ) ) {
53784             return ;
53785         }
53786         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
53787         //{
53788             // We must check the elements firstly using the Id and then the name.
53789         var oTextarea = document.getElementById( this.getId() );
53790         
53791         var colElementsByName = document.getElementsByName( this.getId() ) ;
53792          
53793         oTextarea.style.display = 'none' ;
53794
53795         if ( oTextarea.tabIndex ) {            
53796             this.TabIndex = oTextarea.tabIndex ;
53797         }
53798         
53799         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
53800         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
53801         this.frame = Roo.get(this.getId() + '___Frame')
53802     },
53803     
53804     _getConfigHtml : function()
53805     {
53806         var sConfig = '' ;
53807
53808         for ( var o in this.fckconfig ) {
53809             sConfig += sConfig.length > 0  ? '&amp;' : '';
53810             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
53811         }
53812
53813         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
53814     },
53815     
53816     
53817     _getIFrameHtml : function()
53818     {
53819         var sFile = 'fckeditor.html' ;
53820         /* no idea what this is about..
53821         try
53822         {
53823             if ( (/fcksource=true/i).test( window.top.location.search ) )
53824                 sFile = 'fckeditor.original.html' ;
53825         }
53826         catch (e) { 
53827         */
53828
53829         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
53830         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
53831         
53832         
53833         var html = '<iframe id="' + this.getId() +
53834             '___Frame" src="' + sLink +
53835             '" width="' + this.width +
53836             '" height="' + this.height + '"' +
53837             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
53838             ' frameborder="0" scrolling="no"></iframe>' ;
53839
53840         return html ;
53841     },
53842     
53843     _insertHtmlBefore : function( html, element )
53844     {
53845         if ( element.insertAdjacentHTML )       {
53846             // IE
53847             element.insertAdjacentHTML( 'beforeBegin', html ) ;
53848         } else { // Gecko
53849             var oRange = document.createRange() ;
53850             oRange.setStartBefore( element ) ;
53851             var oFragment = oRange.createContextualFragment( html );
53852             element.parentNode.insertBefore( oFragment, element ) ;
53853         }
53854     }
53855     
53856     
53857   
53858     
53859     
53860     
53861     
53862
53863 });
53864
53865 //Roo.reg('fckeditor', Roo.form.FCKeditor);
53866
53867 function FCKeditor_OnComplete(editorInstance){
53868     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
53869     f.fckEditor = editorInstance;
53870     //console.log("loaded");
53871     f.fireEvent('editorinit', f, editorInstance);
53872
53873   
53874
53875  
53876
53877
53878
53879
53880
53881
53882
53883
53884
53885
53886
53887
53888
53889
53890
53891 //<script type="text/javascript">
53892 /**
53893  * @class Roo.form.GridField
53894  * @extends Roo.form.Field
53895  * Embed a grid (or editable grid into a form)
53896  * STATUS ALPHA
53897  * 
53898  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
53899  * it needs 
53900  * xgrid.store = Roo.data.Store
53901  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
53902  * xgrid.store.reader = Roo.data.JsonReader 
53903  * 
53904  * 
53905  * @constructor
53906  * Creates a new GridField
53907  * @param {Object} config Configuration options
53908  */
53909 Roo.form.GridField = function(config){
53910     Roo.form.GridField.superclass.constructor.call(this, config);
53911      
53912 };
53913
53914 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
53915     /**
53916      * @cfg {Number} width  - used to restrict width of grid..
53917      */
53918     width : 100,
53919     /**
53920      * @cfg {Number} height - used to restrict height of grid..
53921      */
53922     height : 50,
53923      /**
53924      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
53925          * 
53926          *}
53927      */
53928     xgrid : false, 
53929     /**
53930      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
53931      * {tag: "input", type: "checkbox", autocomplete: "off"})
53932      */
53933    // defaultAutoCreate : { tag: 'div' },
53934     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
53935     /**
53936      * @cfg {String} addTitle Text to include for adding a title.
53937      */
53938     addTitle : false,
53939     //
53940     onResize : function(){
53941         Roo.form.Field.superclass.onResize.apply(this, arguments);
53942     },
53943
53944     initEvents : function(){
53945         // Roo.form.Checkbox.superclass.initEvents.call(this);
53946         // has no events...
53947        
53948     },
53949
53950
53951     getResizeEl : function(){
53952         return this.wrap;
53953     },
53954
53955     getPositionEl : function(){
53956         return this.wrap;
53957     },
53958
53959     // private
53960     onRender : function(ct, position){
53961         
53962         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
53963         var style = this.style;
53964         delete this.style;
53965         
53966         Roo.form.GridField.superclass.onRender.call(this, ct, position);
53967         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
53968         this.viewEl = this.wrap.createChild({ tag: 'div' });
53969         if (style) {
53970             this.viewEl.applyStyles(style);
53971         }
53972         if (this.width) {
53973             this.viewEl.setWidth(this.width);
53974         }
53975         if (this.height) {
53976             this.viewEl.setHeight(this.height);
53977         }
53978         //if(this.inputValue !== undefined){
53979         //this.setValue(this.value);
53980         
53981         
53982         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
53983         
53984         
53985         this.grid.render();
53986         this.grid.getDataSource().on('remove', this.refreshValue, this);
53987         this.grid.getDataSource().on('update', this.refreshValue, this);
53988         this.grid.on('afteredit', this.refreshValue, this);
53989  
53990     },
53991      
53992     
53993     /**
53994      * Sets the value of the item. 
53995      * @param {String} either an object  or a string..
53996      */
53997     setValue : function(v){
53998         //this.value = v;
53999         v = v || []; // empty set..
54000         // this does not seem smart - it really only affects memoryproxy grids..
54001         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
54002             var ds = this.grid.getDataSource();
54003             // assumes a json reader..
54004             var data = {}
54005             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
54006             ds.loadData( data);
54007         }
54008         // clear selection so it does not get stale.
54009         if (this.grid.sm) { 
54010             this.grid.sm.clearSelections();
54011         }
54012         
54013         Roo.form.GridField.superclass.setValue.call(this, v);
54014         this.refreshValue();
54015         // should load data in the grid really....
54016     },
54017     
54018     // private
54019     refreshValue: function() {
54020          var val = [];
54021         this.grid.getDataSource().each(function(r) {
54022             val.push(r.data);
54023         });
54024         this.el.dom.value = Roo.encode(val);
54025     }
54026     
54027      
54028     
54029     
54030 });/*
54031  * Based on:
54032  * Ext JS Library 1.1.1
54033  * Copyright(c) 2006-2007, Ext JS, LLC.
54034  *
54035  * Originally Released Under LGPL - original licence link has changed is not relivant.
54036  *
54037  * Fork - LGPL
54038  * <script type="text/javascript">
54039  */
54040 /**
54041  * @class Roo.form.DisplayField
54042  * @extends Roo.form.Field
54043  * A generic Field to display non-editable data.
54044  * @cfg {Boolean} closable (true|false) default false
54045  * @constructor
54046  * Creates a new Display Field item.
54047  * @param {Object} config Configuration options
54048  */
54049 Roo.form.DisplayField = function(config){
54050     Roo.form.DisplayField.superclass.constructor.call(this, config);
54051     
54052     this.addEvents({
54053         /**
54054          * @event close
54055          * Fires after the click the close btn
54056              * @param {Roo.form.DisplayField} this
54057              */
54058         close : true
54059     });
54060 };
54061
54062 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
54063     inputType:      'hidden',
54064     allowBlank:     true,
54065     readOnly:         true,
54066     
54067  
54068     /**
54069      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
54070      */
54071     focusClass : undefined,
54072     /**
54073      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
54074      */
54075     fieldClass: 'x-form-field',
54076     
54077      /**
54078      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
54079      */
54080     valueRenderer: undefined,
54081     
54082     width: 100,
54083     /**
54084      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
54085      * {tag: "input", type: "checkbox", autocomplete: "off"})
54086      */
54087      
54088  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
54089  
54090     closable : false,
54091     
54092     onResize : function(){
54093         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
54094         
54095     },
54096
54097     initEvents : function(){
54098         // Roo.form.Checkbox.superclass.initEvents.call(this);
54099         // has no events...
54100         
54101         if(this.closable){
54102             this.closeEl.on('click', this.onClose, this);
54103         }
54104        
54105     },
54106
54107
54108     getResizeEl : function(){
54109         return this.wrap;
54110     },
54111
54112     getPositionEl : function(){
54113         return this.wrap;
54114     },
54115
54116     // private
54117     onRender : function(ct, position){
54118         
54119         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
54120         //if(this.inputValue !== undefined){
54121         this.wrap = this.el.wrap();
54122         
54123         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
54124         
54125         if(this.closable){
54126             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
54127         }
54128         
54129         if (this.bodyStyle) {
54130             this.viewEl.applyStyles(this.bodyStyle);
54131         }
54132         //this.viewEl.setStyle('padding', '2px');
54133         
54134         this.setValue(this.value);
54135         
54136     },
54137 /*
54138     // private
54139     initValue : Roo.emptyFn,
54140
54141   */
54142
54143         // private
54144     onClick : function(){
54145         
54146     },
54147
54148     /**
54149      * Sets the checked state of the checkbox.
54150      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
54151      */
54152     setValue : function(v){
54153         this.value = v;
54154         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
54155         // this might be called before we have a dom element..
54156         if (!this.viewEl) {
54157             return;
54158         }
54159         this.viewEl.dom.innerHTML = html;
54160         Roo.form.DisplayField.superclass.setValue.call(this, v);
54161
54162     },
54163     
54164     onClose : function(e)
54165     {
54166         e.preventDefault();
54167         
54168         this.fireEvent('close', this);
54169     }
54170 });/*
54171  * 
54172  * Licence- LGPL
54173  * 
54174  */
54175
54176 /**
54177  * @class Roo.form.DayPicker
54178  * @extends Roo.form.Field
54179  * A Day picker show [M] [T] [W] ....
54180  * @constructor
54181  * Creates a new Day Picker
54182  * @param {Object} config Configuration options
54183  */
54184 Roo.form.DayPicker= function(config){
54185     Roo.form.DayPicker.superclass.constructor.call(this, config);
54186      
54187 };
54188
54189 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
54190     /**
54191      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
54192      */
54193     focusClass : undefined,
54194     /**
54195      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
54196      */
54197     fieldClass: "x-form-field",
54198    
54199     /**
54200      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
54201      * {tag: "input", type: "checkbox", autocomplete: "off"})
54202      */
54203     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
54204     
54205    
54206     actionMode : 'viewEl', 
54207     //
54208     // private
54209  
54210     inputType : 'hidden',
54211     
54212      
54213     inputElement: false, // real input element?
54214     basedOn: false, // ????
54215     
54216     isFormField: true, // not sure where this is needed!!!!
54217
54218     onResize : function(){
54219         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
54220         if(!this.boxLabel){
54221             this.el.alignTo(this.wrap, 'c-c');
54222         }
54223     },
54224
54225     initEvents : function(){
54226         Roo.form.Checkbox.superclass.initEvents.call(this);
54227         this.el.on("click", this.onClick,  this);
54228         this.el.on("change", this.onClick,  this);
54229     },
54230
54231
54232     getResizeEl : function(){
54233         return this.wrap;
54234     },
54235
54236     getPositionEl : function(){
54237         return this.wrap;
54238     },
54239
54240     
54241     // private
54242     onRender : function(ct, position){
54243         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
54244        
54245         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
54246         
54247         var r1 = '<table><tr>';
54248         var r2 = '<tr class="x-form-daypick-icons">';
54249         for (var i=0; i < 7; i++) {
54250             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
54251             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
54252         }
54253         
54254         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
54255         viewEl.select('img').on('click', this.onClick, this);
54256         this.viewEl = viewEl;   
54257         
54258         
54259         // this will not work on Chrome!!!
54260         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
54261         this.el.on('propertychange', this.setFromHidden,  this);  //ie
54262         
54263         
54264           
54265
54266     },
54267
54268     // private
54269     initValue : Roo.emptyFn,
54270
54271     /**
54272      * Returns the checked state of the checkbox.
54273      * @return {Boolean} True if checked, else false
54274      */
54275     getValue : function(){
54276         return this.el.dom.value;
54277         
54278     },
54279
54280         // private
54281     onClick : function(e){ 
54282         //this.setChecked(!this.checked);
54283         Roo.get(e.target).toggleClass('x-menu-item-checked');
54284         this.refreshValue();
54285         //if(this.el.dom.checked != this.checked){
54286         //    this.setValue(this.el.dom.checked);
54287        // }
54288     },
54289     
54290     // private
54291     refreshValue : function()
54292     {
54293         var val = '';
54294         this.viewEl.select('img',true).each(function(e,i,n)  {
54295             val += e.is(".x-menu-item-checked") ? String(n) : '';
54296         });
54297         this.setValue(val, true);
54298     },
54299
54300     /**
54301      * Sets the checked state of the checkbox.
54302      * On is always based on a string comparison between inputValue and the param.
54303      * @param {Boolean/String} value - the value to set 
54304      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
54305      */
54306     setValue : function(v,suppressEvent){
54307         if (!this.el.dom) {
54308             return;
54309         }
54310         var old = this.el.dom.value ;
54311         this.el.dom.value = v;
54312         if (suppressEvent) {
54313             return ;
54314         }
54315          
54316         // update display..
54317         this.viewEl.select('img',true).each(function(e,i,n)  {
54318             
54319             var on = e.is(".x-menu-item-checked");
54320             var newv = v.indexOf(String(n)) > -1;
54321             if (on != newv) {
54322                 e.toggleClass('x-menu-item-checked');
54323             }
54324             
54325         });
54326         
54327         
54328         this.fireEvent('change', this, v, old);
54329         
54330         
54331     },
54332    
54333     // handle setting of hidden value by some other method!!?!?
54334     setFromHidden: function()
54335     {
54336         if(!this.el){
54337             return;
54338         }
54339         //console.log("SET FROM HIDDEN");
54340         //alert('setFrom hidden');
54341         this.setValue(this.el.dom.value);
54342     },
54343     
54344     onDestroy : function()
54345     {
54346         if(this.viewEl){
54347             Roo.get(this.viewEl).remove();
54348         }
54349          
54350         Roo.form.DayPicker.superclass.onDestroy.call(this);
54351     }
54352
54353 });/*
54354  * RooJS Library 1.1.1
54355  * Copyright(c) 2008-2011  Alan Knowles
54356  *
54357  * License - LGPL
54358  */
54359  
54360
54361 /**
54362  * @class Roo.form.ComboCheck
54363  * @extends Roo.form.ComboBox
54364  * A combobox for multiple select items.
54365  *
54366  * FIXME - could do with a reset button..
54367  * 
54368  * @constructor
54369  * Create a new ComboCheck
54370  * @param {Object} config Configuration options
54371  */
54372 Roo.form.ComboCheck = function(config){
54373     Roo.form.ComboCheck.superclass.constructor.call(this, config);
54374     // should verify some data...
54375     // like
54376     // hiddenName = required..
54377     // displayField = required
54378     // valudField == required
54379     var req= [ 'hiddenName', 'displayField', 'valueField' ];
54380     var _t = this;
54381     Roo.each(req, function(e) {
54382         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
54383             throw "Roo.form.ComboCheck : missing value for: " + e;
54384         }
54385     });
54386     
54387     
54388 };
54389
54390 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
54391      
54392      
54393     editable : false,
54394      
54395     selectedClass: 'x-menu-item-checked', 
54396     
54397     // private
54398     onRender : function(ct, position){
54399         var _t = this;
54400         
54401         
54402         
54403         if(!this.tpl){
54404             var cls = 'x-combo-list';
54405
54406             
54407             this.tpl =  new Roo.Template({
54408                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
54409                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
54410                    '<span>{' + this.displayField + '}</span>' +
54411                     '</div>' 
54412                 
54413             });
54414         }
54415  
54416         
54417         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
54418         this.view.singleSelect = false;
54419         this.view.multiSelect = true;
54420         this.view.toggleSelect = true;
54421         this.pageTb.add(new Roo.Toolbar.Fill(), {
54422             
54423             text: 'Done',
54424             handler: function()
54425             {
54426                 _t.collapse();
54427             }
54428         });
54429     },
54430     
54431     onViewOver : function(e, t){
54432         // do nothing...
54433         return;
54434         
54435     },
54436     
54437     onViewClick : function(doFocus,index){
54438         return;
54439         
54440     },
54441     select: function () {
54442         //Roo.log("SELECT CALLED");
54443     },
54444      
54445     selectByValue : function(xv, scrollIntoView){
54446         var ar = this.getValueArray();
54447         var sels = [];
54448         
54449         Roo.each(ar, function(v) {
54450             if(v === undefined || v === null){
54451                 return;
54452             }
54453             var r = this.findRecord(this.valueField, v);
54454             if(r){
54455                 sels.push(this.store.indexOf(r))
54456                 
54457             }
54458         },this);
54459         this.view.select(sels);
54460         return false;
54461     },
54462     
54463     
54464     
54465     onSelect : function(record, index){
54466        // Roo.log("onselect Called");
54467        // this is only called by the clear button now..
54468         this.view.clearSelections();
54469         this.setValue('[]');
54470         if (this.value != this.valueBefore) {
54471             this.fireEvent('change', this, this.value, this.valueBefore);
54472             this.valueBefore = this.value;
54473         }
54474     },
54475     getValueArray : function()
54476     {
54477         var ar = [] ;
54478         
54479         try {
54480             //Roo.log(this.value);
54481             if (typeof(this.value) == 'undefined') {
54482                 return [];
54483             }
54484             var ar = Roo.decode(this.value);
54485             return  ar instanceof Array ? ar : []; //?? valid?
54486             
54487         } catch(e) {
54488             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
54489             return [];
54490         }
54491          
54492     },
54493     expand : function ()
54494     {
54495         
54496         Roo.form.ComboCheck.superclass.expand.call(this);
54497         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
54498         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
54499         
54500
54501     },
54502     
54503     collapse : function(){
54504         Roo.form.ComboCheck.superclass.collapse.call(this);
54505         var sl = this.view.getSelectedIndexes();
54506         var st = this.store;
54507         var nv = [];
54508         var tv = [];
54509         var r;
54510         Roo.each(sl, function(i) {
54511             r = st.getAt(i);
54512             nv.push(r.get(this.valueField));
54513         },this);
54514         this.setValue(Roo.encode(nv));
54515         if (this.value != this.valueBefore) {
54516
54517             this.fireEvent('change', this, this.value, this.valueBefore);
54518             this.valueBefore = this.value;
54519         }
54520         
54521     },
54522     
54523     setValue : function(v){
54524         // Roo.log(v);
54525         this.value = v;
54526         
54527         var vals = this.getValueArray();
54528         var tv = [];
54529         Roo.each(vals, function(k) {
54530             var r = this.findRecord(this.valueField, k);
54531             if(r){
54532                 tv.push(r.data[this.displayField]);
54533             }else if(this.valueNotFoundText !== undefined){
54534                 tv.push( this.valueNotFoundText );
54535             }
54536         },this);
54537        // Roo.log(tv);
54538         
54539         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
54540         this.hiddenField.value = v;
54541         this.value = v;
54542     }
54543     
54544 });/*
54545  * Based on:
54546  * Ext JS Library 1.1.1
54547  * Copyright(c) 2006-2007, Ext JS, LLC.
54548  *
54549  * Originally Released Under LGPL - original licence link has changed is not relivant.
54550  *
54551  * Fork - LGPL
54552  * <script type="text/javascript">
54553  */
54554  
54555 /**
54556  * @class Roo.form.Signature
54557  * @extends Roo.form.Field
54558  * Signature field.  
54559  * @constructor
54560  * 
54561  * @param {Object} config Configuration options
54562  */
54563
54564 Roo.form.Signature = function(config){
54565     Roo.form.Signature.superclass.constructor.call(this, config);
54566     
54567     this.addEvents({// not in used??
54568          /**
54569          * @event confirm
54570          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
54571              * @param {Roo.form.Signature} combo This combo box
54572              */
54573         'confirm' : true,
54574         /**
54575          * @event reset
54576          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
54577              * @param {Roo.form.ComboBox} combo This combo box
54578              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
54579              */
54580         'reset' : true
54581     });
54582 };
54583
54584 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
54585     /**
54586      * @cfg {Object} labels Label to use when rendering a form.
54587      * defaults to 
54588      * labels : { 
54589      *      clear : "Clear",
54590      *      confirm : "Confirm"
54591      *  }
54592      */
54593     labels : { 
54594         clear : "Clear",
54595         confirm : "Confirm"
54596     },
54597     /**
54598      * @cfg {Number} width The signature panel width (defaults to 300)
54599      */
54600     width: 300,
54601     /**
54602      * @cfg {Number} height The signature panel height (defaults to 100)
54603      */
54604     height : 100,
54605     /**
54606      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
54607      */
54608     allowBlank : false,
54609     
54610     //private
54611     // {Object} signPanel The signature SVG panel element (defaults to {})
54612     signPanel : {},
54613     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
54614     isMouseDown : false,
54615     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
54616     isConfirmed : false,
54617     // {String} signatureTmp SVG mapping string (defaults to empty string)
54618     signatureTmp : '',
54619     
54620     
54621     defaultAutoCreate : { // modified by initCompnoent..
54622         tag: "input",
54623         type:"hidden"
54624     },
54625
54626     // private
54627     onRender : function(ct, position){
54628         
54629         Roo.form.Signature.superclass.onRender.call(this, ct, position);
54630         
54631         this.wrap = this.el.wrap({
54632             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
54633         });
54634         
54635         this.createToolbar(this);
54636         this.signPanel = this.wrap.createChild({
54637                 tag: 'div',
54638                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
54639             }, this.el
54640         );
54641             
54642         this.svgID = Roo.id();
54643         this.svgEl = this.signPanel.createChild({
54644               xmlns : 'http://www.w3.org/2000/svg',
54645               tag : 'svg',
54646               id : this.svgID + "-svg",
54647               width: this.width,
54648               height: this.height,
54649               viewBox: '0 0 '+this.width+' '+this.height,
54650               cn : [
54651                 {
54652                     tag: "rect",
54653                     id: this.svgID + "-svg-r",
54654                     width: this.width,
54655                     height: this.height,
54656                     fill: "#ffa"
54657                 },
54658                 {
54659                     tag: "line",
54660                     id: this.svgID + "-svg-l",
54661                     x1: "0", // start
54662                     y1: (this.height*0.8), // start set the line in 80% of height
54663                     x2: this.width, // end
54664                     y2: (this.height*0.8), // end set the line in 80% of height
54665                     'stroke': "#666",
54666                     'stroke-width': "1",
54667                     'stroke-dasharray': "3",
54668                     'shape-rendering': "crispEdges",
54669                     'pointer-events': "none"
54670                 },
54671                 {
54672                     tag: "path",
54673                     id: this.svgID + "-svg-p",
54674                     'stroke': "navy",
54675                     'stroke-width': "3",
54676                     'fill': "none",
54677                     'pointer-events': 'none'
54678                 }
54679               ]
54680         });
54681         this.createSVG();
54682         this.svgBox = this.svgEl.dom.getScreenCTM();
54683     },
54684     createSVG : function(){ 
54685         var svg = this.signPanel;
54686         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
54687         var t = this;
54688
54689         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
54690         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
54691         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
54692         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
54693         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
54694         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
54695         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
54696         
54697     },
54698     isTouchEvent : function(e){
54699         return e.type.match(/^touch/);
54700     },
54701     getCoords : function (e) {
54702         var pt    = this.svgEl.dom.createSVGPoint();
54703         pt.x = e.clientX; 
54704         pt.y = e.clientY;
54705         if (this.isTouchEvent(e)) {
54706             pt.x =  e.targetTouches[0].clientX;
54707             pt.y = e.targetTouches[0].clientY;
54708         }
54709         var a = this.svgEl.dom.getScreenCTM();
54710         var b = a.inverse();
54711         var mx = pt.matrixTransform(b);
54712         return mx.x + ',' + mx.y;
54713     },
54714     //mouse event headler 
54715     down : function (e) {
54716         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
54717         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
54718         
54719         this.isMouseDown = true;
54720         
54721         e.preventDefault();
54722     },
54723     move : function (e) {
54724         if (this.isMouseDown) {
54725             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
54726             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
54727         }
54728         
54729         e.preventDefault();
54730     },
54731     up : function (e) {
54732         this.isMouseDown = false;
54733         var sp = this.signatureTmp.split(' ');
54734         
54735         if(sp.length > 1){
54736             if(!sp[sp.length-2].match(/^L/)){
54737                 sp.pop();
54738                 sp.pop();
54739                 sp.push("");
54740                 this.signatureTmp = sp.join(" ");
54741             }
54742         }
54743         if(this.getValue() != this.signatureTmp){
54744             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
54745             this.isConfirmed = false;
54746         }
54747         e.preventDefault();
54748     },
54749     
54750     /**
54751      * Protected method that will not generally be called directly. It
54752      * is called when the editor creates its toolbar. Override this method if you need to
54753      * add custom toolbar buttons.
54754      * @param {HtmlEditor} editor
54755      */
54756     createToolbar : function(editor){
54757          function btn(id, toggle, handler){
54758             var xid = fid + '-'+ id ;
54759             return {
54760                 id : xid,
54761                 cmd : id,
54762                 cls : 'x-btn-icon x-edit-'+id,
54763                 enableToggle:toggle !== false,
54764                 scope: editor, // was editor...
54765                 handler:handler||editor.relayBtnCmd,
54766                 clickEvent:'mousedown',
54767                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
54768                 tabIndex:-1
54769             };
54770         }
54771         
54772         
54773         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
54774         this.tb = tb;
54775         this.tb.add(
54776            {
54777                 cls : ' x-signature-btn x-signature-'+id,
54778                 scope: editor, // was editor...
54779                 handler: this.reset,
54780                 clickEvent:'mousedown',
54781                 text: this.labels.clear
54782             },
54783             {
54784                  xtype : 'Fill',
54785                  xns: Roo.Toolbar
54786             }, 
54787             {
54788                 cls : '  x-signature-btn x-signature-'+id,
54789                 scope: editor, // was editor...
54790                 handler: this.confirmHandler,
54791                 clickEvent:'mousedown',
54792                 text: this.labels.confirm
54793             }
54794         );
54795     
54796     },
54797     //public
54798     /**
54799      * when user is clicked confirm then show this image.....
54800      * 
54801      * @return {String} Image Data URI
54802      */
54803     getImageDataURI : function(){
54804         var svg = this.svgEl.dom.parentNode.innerHTML;
54805         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
54806         return src; 
54807     },
54808     /**
54809      * 
54810      * @return {Boolean} this.isConfirmed
54811      */
54812     getConfirmed : function(){
54813         return this.isConfirmed;
54814     },
54815     /**
54816      * 
54817      * @return {Number} this.width
54818      */
54819     getWidth : function(){
54820         return this.width;
54821     },
54822     /**
54823      * 
54824      * @return {Number} this.height
54825      */
54826     getHeight : function(){
54827         return this.height;
54828     },
54829     // private
54830     getSignature : function(){
54831         return this.signatureTmp;
54832     },
54833     // private
54834     reset : function(){
54835         this.signatureTmp = '';
54836         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
54837         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
54838         this.isConfirmed = false;
54839         Roo.form.Signature.superclass.reset.call(this);
54840     },
54841     setSignature : function(s){
54842         this.signatureTmp = s;
54843         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
54844         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
54845         this.setValue(s);
54846         this.isConfirmed = false;
54847         Roo.form.Signature.superclass.reset.call(this);
54848     }, 
54849     test : function(){
54850 //        Roo.log(this.signPanel.dom.contentWindow.up())
54851     },
54852     //private
54853     setConfirmed : function(){
54854         
54855         
54856         
54857 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
54858     },
54859     // private
54860     confirmHandler : function(){
54861         if(!this.getSignature()){
54862             return;
54863         }
54864         
54865         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
54866         this.setValue(this.getSignature());
54867         this.isConfirmed = true;
54868         
54869         this.fireEvent('confirm', this);
54870     },
54871     // private
54872     // Subclasses should provide the validation implementation by overriding this
54873     validateValue : function(value){
54874         if(this.allowBlank){
54875             return true;
54876         }
54877         
54878         if(this.isConfirmed){
54879             return true;
54880         }
54881         return false;
54882     }
54883 });/*
54884  * Based on:
54885  * Ext JS Library 1.1.1
54886  * Copyright(c) 2006-2007, Ext JS, LLC.
54887  *
54888  * Originally Released Under LGPL - original licence link has changed is not relivant.
54889  *
54890  * Fork - LGPL
54891  * <script type="text/javascript">
54892  */
54893  
54894
54895 /**
54896  * @class Roo.form.ComboBox
54897  * @extends Roo.form.TriggerField
54898  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
54899  * @constructor
54900  * Create a new ComboBox.
54901  * @param {Object} config Configuration options
54902  */
54903 Roo.form.Select = function(config){
54904     Roo.form.Select.superclass.constructor.call(this, config);
54905      
54906 };
54907
54908 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
54909     /**
54910      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
54911      */
54912     /**
54913      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
54914      * rendering into an Roo.Editor, defaults to false)
54915      */
54916     /**
54917      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
54918      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
54919      */
54920     /**
54921      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
54922      */
54923     /**
54924      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
54925      * the dropdown list (defaults to undefined, with no header element)
54926      */
54927
54928      /**
54929      * @cfg {String/Roo.Template} tpl The template to use to render the output
54930      */
54931      
54932     // private
54933     defaultAutoCreate : {tag: "select"  },
54934     /**
54935      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
54936      */
54937     listWidth: undefined,
54938     /**
54939      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
54940      * mode = 'remote' or 'text' if mode = 'local')
54941      */
54942     displayField: undefined,
54943     /**
54944      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
54945      * mode = 'remote' or 'value' if mode = 'local'). 
54946      * Note: use of a valueField requires the user make a selection
54947      * in order for a value to be mapped.
54948      */
54949     valueField: undefined,
54950     
54951     
54952     /**
54953      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
54954      * field's data value (defaults to the underlying DOM element's name)
54955      */
54956     hiddenName: undefined,
54957     /**
54958      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
54959      */
54960     listClass: '',
54961     /**
54962      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
54963      */
54964     selectedClass: 'x-combo-selected',
54965     /**
54966      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
54967      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
54968      * which displays a downward arrow icon).
54969      */
54970     triggerClass : 'x-form-arrow-trigger',
54971     /**
54972      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
54973      */
54974     shadow:'sides',
54975     /**
54976      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
54977      * anchor positions (defaults to 'tl-bl')
54978      */
54979     listAlign: 'tl-bl?',
54980     /**
54981      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
54982      */
54983     maxHeight: 300,
54984     /**
54985      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
54986      * query specified by the allQuery config option (defaults to 'query')
54987      */
54988     triggerAction: 'query',
54989     /**
54990      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
54991      * (defaults to 4, does not apply if editable = false)
54992      */
54993     minChars : 4,
54994     /**
54995      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
54996      * delay (typeAheadDelay) if it matches a known value (defaults to false)
54997      */
54998     typeAhead: false,
54999     /**
55000      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
55001      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
55002      */
55003     queryDelay: 500,
55004     /**
55005      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
55006      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
55007      */
55008     pageSize: 0,
55009     /**
55010      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
55011      * when editable = true (defaults to false)
55012      */
55013     selectOnFocus:false,
55014     /**
55015      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
55016      */
55017     queryParam: 'query',
55018     /**
55019      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
55020      * when mode = 'remote' (defaults to 'Loading...')
55021      */
55022     loadingText: 'Loading...',
55023     /**
55024      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
55025      */
55026     resizable: false,
55027     /**
55028      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
55029      */
55030     handleHeight : 8,
55031     /**
55032      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
55033      * traditional select (defaults to true)
55034      */
55035     editable: true,
55036     /**
55037      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
55038      */
55039     allQuery: '',
55040     /**
55041      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
55042      */
55043     mode: 'remote',
55044     /**
55045      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
55046      * listWidth has a higher value)
55047      */
55048     minListWidth : 70,
55049     /**
55050      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
55051      * allow the user to set arbitrary text into the field (defaults to false)
55052      */
55053     forceSelection:false,
55054     /**
55055      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
55056      * if typeAhead = true (defaults to 250)
55057      */
55058     typeAheadDelay : 250,
55059     /**
55060      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
55061      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
55062      */
55063     valueNotFoundText : undefined,
55064     
55065     /**
55066      * @cfg {String} defaultValue The value displayed after loading the store.
55067      */
55068     defaultValue: '',
55069     
55070     /**
55071      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
55072      */
55073     blockFocus : false,
55074     
55075     /**
55076      * @cfg {Boolean} disableClear Disable showing of clear button.
55077      */
55078     disableClear : false,
55079     /**
55080      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
55081      */
55082     alwaysQuery : false,
55083     
55084     //private
55085     addicon : false,
55086     editicon: false,
55087     
55088     // element that contains real text value.. (when hidden is used..)
55089      
55090     // private
55091     onRender : function(ct, position){
55092         Roo.form.Field.prototype.onRender.call(this, ct, position);
55093         
55094         if(this.store){
55095             this.store.on('beforeload', this.onBeforeLoad, this);
55096             this.store.on('load', this.onLoad, this);
55097             this.store.on('loadexception', this.onLoadException, this);
55098             this.store.load({});
55099         }
55100         
55101         
55102         
55103     },
55104
55105     // private
55106     initEvents : function(){
55107         //Roo.form.ComboBox.superclass.initEvents.call(this);
55108  
55109     },
55110
55111     onDestroy : function(){
55112        
55113         if(this.store){
55114             this.store.un('beforeload', this.onBeforeLoad, this);
55115             this.store.un('load', this.onLoad, this);
55116             this.store.un('loadexception', this.onLoadException, this);
55117         }
55118         //Roo.form.ComboBox.superclass.onDestroy.call(this);
55119     },
55120
55121     // private
55122     fireKey : function(e){
55123         if(e.isNavKeyPress() && !this.list.isVisible()){
55124             this.fireEvent("specialkey", this, e);
55125         }
55126     },
55127
55128     // private
55129     onResize: function(w, h){
55130         
55131         return; 
55132     
55133         
55134     },
55135
55136     /**
55137      * Allow or prevent the user from directly editing the field text.  If false is passed,
55138      * the user will only be able to select from the items defined in the dropdown list.  This method
55139      * is the runtime equivalent of setting the 'editable' config option at config time.
55140      * @param {Boolean} value True to allow the user to directly edit the field text
55141      */
55142     setEditable : function(value){
55143          
55144     },
55145
55146     // private
55147     onBeforeLoad : function(){
55148         
55149         Roo.log("Select before load");
55150         return;
55151     
55152         this.innerList.update(this.loadingText ?
55153                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
55154         //this.restrictHeight();
55155         this.selectedIndex = -1;
55156     },
55157
55158     // private
55159     onLoad : function(){
55160
55161     
55162         var dom = this.el.dom;
55163         dom.innerHTML = '';
55164          var od = dom.ownerDocument;
55165          
55166         if (this.emptyText) {
55167             var op = od.createElement('option');
55168             op.setAttribute('value', '');
55169             op.innerHTML = String.format('{0}', this.emptyText);
55170             dom.appendChild(op);
55171         }
55172         if(this.store.getCount() > 0){
55173            
55174             var vf = this.valueField;
55175             var df = this.displayField;
55176             this.store.data.each(function(r) {
55177                 // which colmsn to use... testing - cdoe / title..
55178                 var op = od.createElement('option');
55179                 op.setAttribute('value', r.data[vf]);
55180                 op.innerHTML = String.format('{0}', r.data[df]);
55181                 dom.appendChild(op);
55182             });
55183             if (typeof(this.defaultValue != 'undefined')) {
55184                 this.setValue(this.defaultValue);
55185             }
55186             
55187              
55188         }else{
55189             //this.onEmptyResults();
55190         }
55191         //this.el.focus();
55192     },
55193     // private
55194     onLoadException : function()
55195     {
55196         dom.innerHTML = '';
55197             
55198         Roo.log("Select on load exception");
55199         return;
55200     
55201         this.collapse();
55202         Roo.log(this.store.reader.jsonData);
55203         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
55204             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
55205         }
55206         
55207         
55208     },
55209     // private
55210     onTypeAhead : function(){
55211          
55212     },
55213
55214     // private
55215     onSelect : function(record, index){
55216         Roo.log('on select?');
55217         return;
55218         if(this.fireEvent('beforeselect', this, record, index) !== false){
55219             this.setFromData(index > -1 ? record.data : false);
55220             this.collapse();
55221             this.fireEvent('select', this, record, index);
55222         }
55223     },
55224
55225     /**
55226      * Returns the currently selected field value or empty string if no value is set.
55227      * @return {String} value The selected value
55228      */
55229     getValue : function(){
55230         var dom = this.el.dom;
55231         this.value = dom.options[dom.selectedIndex].value;
55232         return this.value;
55233         
55234     },
55235
55236     /**
55237      * Clears any text/value currently set in the field
55238      */
55239     clearValue : function(){
55240         this.value = '';
55241         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
55242         
55243     },
55244
55245     /**
55246      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
55247      * will be displayed in the field.  If the value does not match the data value of an existing item,
55248      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
55249      * Otherwise the field will be blank (although the value will still be set).
55250      * @param {String} value The value to match
55251      */
55252     setValue : function(v){
55253         var d = this.el.dom;
55254         for (var i =0; i < d.options.length;i++) {
55255             if (v == d.options[i].value) {
55256                 d.selectedIndex = i;
55257                 this.value = v;
55258                 return;
55259             }
55260         }
55261         this.clearValue();
55262     },
55263     /**
55264      * @property {Object} the last set data for the element
55265      */
55266     
55267     lastData : false,
55268     /**
55269      * Sets the value of the field based on a object which is related to the record format for the store.
55270      * @param {Object} value the value to set as. or false on reset?
55271      */
55272     setFromData : function(o){
55273         Roo.log('setfrom data?');
55274          
55275         
55276         
55277     },
55278     // private
55279     reset : function(){
55280         this.clearValue();
55281     },
55282     // private
55283     findRecord : function(prop, value){
55284         
55285         return false;
55286     
55287         var record;
55288         if(this.store.getCount() > 0){
55289             this.store.each(function(r){
55290                 if(r.data[prop] == value){
55291                     record = r;
55292                     return false;
55293                 }
55294                 return true;
55295             });
55296         }
55297         return record;
55298     },
55299     
55300     getName: function()
55301     {
55302         // returns hidden if it's set..
55303         if (!this.rendered) {return ''};
55304         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
55305         
55306     },
55307      
55308
55309     
55310
55311     // private
55312     onEmptyResults : function(){
55313         Roo.log('empty results');
55314         //this.collapse();
55315     },
55316
55317     /**
55318      * Returns true if the dropdown list is expanded, else false.
55319      */
55320     isExpanded : function(){
55321         return false;
55322     },
55323
55324     /**
55325      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
55326      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
55327      * @param {String} value The data value of the item to select
55328      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
55329      * selected item if it is not currently in view (defaults to true)
55330      * @return {Boolean} True if the value matched an item in the list, else false
55331      */
55332     selectByValue : function(v, scrollIntoView){
55333         Roo.log('select By Value');
55334         return false;
55335     
55336         if(v !== undefined && v !== null){
55337             var r = this.findRecord(this.valueField || this.displayField, v);
55338             if(r){
55339                 this.select(this.store.indexOf(r), scrollIntoView);
55340                 return true;
55341             }
55342         }
55343         return false;
55344     },
55345
55346     /**
55347      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
55348      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
55349      * @param {Number} index The zero-based index of the list item to select
55350      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
55351      * selected item if it is not currently in view (defaults to true)
55352      */
55353     select : function(index, scrollIntoView){
55354         Roo.log('select ');
55355         return  ;
55356         
55357         this.selectedIndex = index;
55358         this.view.select(index);
55359         if(scrollIntoView !== false){
55360             var el = this.view.getNode(index);
55361             if(el){
55362                 this.innerList.scrollChildIntoView(el, false);
55363             }
55364         }
55365     },
55366
55367       
55368
55369     // private
55370     validateBlur : function(){
55371         
55372         return;
55373         
55374     },
55375
55376     // private
55377     initQuery : function(){
55378         this.doQuery(this.getRawValue());
55379     },
55380
55381     // private
55382     doForce : function(){
55383         if(this.el.dom.value.length > 0){
55384             this.el.dom.value =
55385                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
55386              
55387         }
55388     },
55389
55390     /**
55391      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
55392      * query allowing the query action to be canceled if needed.
55393      * @param {String} query The SQL query to execute
55394      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
55395      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
55396      * saved in the current store (defaults to false)
55397      */
55398     doQuery : function(q, forceAll){
55399         
55400         Roo.log('doQuery?');
55401         if(q === undefined || q === null){
55402             q = '';
55403         }
55404         var qe = {
55405             query: q,
55406             forceAll: forceAll,
55407             combo: this,
55408             cancel:false
55409         };
55410         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
55411             return false;
55412         }
55413         q = qe.query;
55414         forceAll = qe.forceAll;
55415         if(forceAll === true || (q.length >= this.minChars)){
55416             if(this.lastQuery != q || this.alwaysQuery){
55417                 this.lastQuery = q;
55418                 if(this.mode == 'local'){
55419                     this.selectedIndex = -1;
55420                     if(forceAll){
55421                         this.store.clearFilter();
55422                     }else{
55423                         this.store.filter(this.displayField, q);
55424                     }
55425                     this.onLoad();
55426                 }else{
55427                     this.store.baseParams[this.queryParam] = q;
55428                     this.store.load({
55429                         params: this.getParams(q)
55430                     });
55431                     this.expand();
55432                 }
55433             }else{
55434                 this.selectedIndex = -1;
55435                 this.onLoad();   
55436             }
55437         }
55438     },
55439
55440     // private
55441     getParams : function(q){
55442         var p = {};
55443         //p[this.queryParam] = q;
55444         if(this.pageSize){
55445             p.start = 0;
55446             p.limit = this.pageSize;
55447         }
55448         return p;
55449     },
55450
55451     /**
55452      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
55453      */
55454     collapse : function(){
55455         
55456     },
55457
55458     // private
55459     collapseIf : function(e){
55460         
55461     },
55462
55463     /**
55464      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
55465      */
55466     expand : function(){
55467         
55468     } ,
55469
55470     // private
55471      
55472
55473     /** 
55474     * @cfg {Boolean} grow 
55475     * @hide 
55476     */
55477     /** 
55478     * @cfg {Number} growMin 
55479     * @hide 
55480     */
55481     /** 
55482     * @cfg {Number} growMax 
55483     * @hide 
55484     */
55485     /**
55486      * @hide
55487      * @method autoSize
55488      */
55489     
55490     setWidth : function()
55491     {
55492         
55493     },
55494     getResizeEl : function(){
55495         return this.el;
55496     }
55497 });//<script type="text/javasscript">
55498  
55499
55500 /**
55501  * @class Roo.DDView
55502  * A DnD enabled version of Roo.View.
55503  * @param {Element/String} container The Element in which to create the View.
55504  * @param {String} tpl The template string used to create the markup for each element of the View
55505  * @param {Object} config The configuration properties. These include all the config options of
55506  * {@link Roo.View} plus some specific to this class.<br>
55507  * <p>
55508  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
55509  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
55510  * <p>
55511  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
55512 .x-view-drag-insert-above {
55513         border-top:1px dotted #3366cc;
55514 }
55515 .x-view-drag-insert-below {
55516         border-bottom:1px dotted #3366cc;
55517 }
55518 </code></pre>
55519  * 
55520  */
55521  
55522 Roo.DDView = function(container, tpl, config) {
55523     Roo.DDView.superclass.constructor.apply(this, arguments);
55524     this.getEl().setStyle("outline", "0px none");
55525     this.getEl().unselectable();
55526     if (this.dragGroup) {
55527         this.setDraggable(this.dragGroup.split(","));
55528     }
55529     if (this.dropGroup) {
55530         this.setDroppable(this.dropGroup.split(","));
55531     }
55532     if (this.deletable) {
55533         this.setDeletable();
55534     }
55535     this.isDirtyFlag = false;
55536         this.addEvents({
55537                 "drop" : true
55538         });
55539 };
55540
55541 Roo.extend(Roo.DDView, Roo.View, {
55542 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
55543 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
55544 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
55545 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
55546
55547         isFormField: true,
55548
55549         reset: Roo.emptyFn,
55550         
55551         clearInvalid: Roo.form.Field.prototype.clearInvalid,
55552
55553         validate: function() {
55554                 return true;
55555         },
55556         
55557         destroy: function() {
55558                 this.purgeListeners();
55559                 this.getEl.removeAllListeners();
55560                 this.getEl().remove();
55561                 if (this.dragZone) {
55562                         if (this.dragZone.destroy) {
55563                                 this.dragZone.destroy();
55564                         }
55565                 }
55566                 if (this.dropZone) {
55567                         if (this.dropZone.destroy) {
55568                                 this.dropZone.destroy();
55569                         }
55570                 }
55571         },
55572
55573 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
55574         getName: function() {
55575                 return this.name;
55576         },
55577
55578 /**     Loads the View from a JSON string representing the Records to put into the Store. */
55579         setValue: function(v) {
55580                 if (!this.store) {
55581                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
55582                 }
55583                 var data = {};
55584                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
55585                 this.store.proxy = new Roo.data.MemoryProxy(data);
55586                 this.store.load();
55587         },
55588
55589 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
55590         getValue: function() {
55591                 var result = '(';
55592                 this.store.each(function(rec) {
55593                         result += rec.id + ',';
55594                 });
55595                 return result.substr(0, result.length - 1) + ')';
55596         },
55597         
55598         getIds: function() {
55599                 var i = 0, result = new Array(this.store.getCount());
55600                 this.store.each(function(rec) {
55601                         result[i++] = rec.id;
55602                 });
55603                 return result;
55604         },
55605         
55606         isDirty: function() {
55607                 return this.isDirtyFlag;
55608         },
55609
55610 /**
55611  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
55612  *      whole Element becomes the target, and this causes the drop gesture to append.
55613  */
55614     getTargetFromEvent : function(e) {
55615                 var target = e.getTarget();
55616                 while ((target !== null) && (target.parentNode != this.el.dom)) {
55617                 target = target.parentNode;
55618                 }
55619                 if (!target) {
55620                         target = this.el.dom.lastChild || this.el.dom;
55621                 }
55622                 return target;
55623     },
55624
55625 /**
55626  *      Create the drag data which consists of an object which has the property "ddel" as
55627  *      the drag proxy element. 
55628  */
55629     getDragData : function(e) {
55630         var target = this.findItemFromChild(e.getTarget());
55631                 if(target) {
55632                         this.handleSelection(e);
55633                         var selNodes = this.getSelectedNodes();
55634             var dragData = {
55635                 source: this,
55636                 copy: this.copy || (this.allowCopy && e.ctrlKey),
55637                 nodes: selNodes,
55638                 records: []
55639                         };
55640                         var selectedIndices = this.getSelectedIndexes();
55641                         for (var i = 0; i < selectedIndices.length; i++) {
55642                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
55643                         }
55644                         if (selNodes.length == 1) {
55645                                 dragData.ddel = target.cloneNode(true); // the div element
55646                         } else {
55647                                 var div = document.createElement('div'); // create the multi element drag "ghost"
55648                                 div.className = 'multi-proxy';
55649                                 for (var i = 0, len = selNodes.length; i < len; i++) {
55650                                         div.appendChild(selNodes[i].cloneNode(true));
55651                                 }
55652                                 dragData.ddel = div;
55653                         }
55654             //console.log(dragData)
55655             //console.log(dragData.ddel.innerHTML)
55656                         return dragData;
55657                 }
55658         //console.log('nodragData')
55659                 return false;
55660     },
55661     
55662 /**     Specify to which ddGroup items in this DDView may be dragged. */
55663     setDraggable: function(ddGroup) {
55664         if (ddGroup instanceof Array) {
55665                 Roo.each(ddGroup, this.setDraggable, this);
55666                 return;
55667         }
55668         if (this.dragZone) {
55669                 this.dragZone.addToGroup(ddGroup);
55670         } else {
55671                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
55672                                 containerScroll: true,
55673                                 ddGroup: ddGroup 
55674
55675                         });
55676 //                      Draggability implies selection. DragZone's mousedown selects the element.
55677                         if (!this.multiSelect) { this.singleSelect = true; }
55678
55679 //                      Wire the DragZone's handlers up to methods in *this*
55680                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
55681                 }
55682     },
55683
55684 /**     Specify from which ddGroup this DDView accepts drops. */
55685     setDroppable: function(ddGroup) {
55686         if (ddGroup instanceof Array) {
55687                 Roo.each(ddGroup, this.setDroppable, this);
55688                 return;
55689         }
55690         if (this.dropZone) {
55691                 this.dropZone.addToGroup(ddGroup);
55692         } else {
55693                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
55694                                 containerScroll: true,
55695                                 ddGroup: ddGroup
55696                         });
55697
55698 //                      Wire the DropZone's handlers up to methods in *this*
55699                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
55700                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
55701                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
55702                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
55703                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
55704                 }
55705     },
55706
55707 /**     Decide whether to drop above or below a View node. */
55708     getDropPoint : function(e, n, dd){
55709         if (n == this.el.dom) { return "above"; }
55710                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
55711                 var c = t + (b - t) / 2;
55712                 var y = Roo.lib.Event.getPageY(e);
55713                 if(y <= c) {
55714                         return "above";
55715                 }else{
55716                         return "below";
55717                 }
55718     },
55719
55720     onNodeEnter : function(n, dd, e, data){
55721                 return false;
55722     },
55723     
55724     onNodeOver : function(n, dd, e, data){
55725                 var pt = this.getDropPoint(e, n, dd);
55726                 // set the insert point style on the target node
55727                 var dragElClass = this.dropNotAllowed;
55728                 if (pt) {
55729                         var targetElClass;
55730                         if (pt == "above"){
55731                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
55732                                 targetElClass = "x-view-drag-insert-above";
55733                         } else {
55734                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
55735                                 targetElClass = "x-view-drag-insert-below";
55736                         }
55737                         if (this.lastInsertClass != targetElClass){
55738                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
55739                                 this.lastInsertClass = targetElClass;
55740                         }
55741                 }
55742                 return dragElClass;
55743         },
55744
55745     onNodeOut : function(n, dd, e, data){
55746                 this.removeDropIndicators(n);
55747     },
55748
55749     onNodeDrop : function(n, dd, e, data){
55750         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
55751                 return false;
55752         }
55753         var pt = this.getDropPoint(e, n, dd);
55754                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
55755                 if (pt == "below") { insertAt++; }
55756                 for (var i = 0; i < data.records.length; i++) {
55757                         var r = data.records[i];
55758                         var dup = this.store.getById(r.id);
55759                         if (dup && (dd != this.dragZone)) {
55760                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
55761                         } else {
55762                                 if (data.copy) {
55763                                         this.store.insert(insertAt++, r.copy());
55764                                 } else {
55765                                         data.source.isDirtyFlag = true;
55766                                         r.store.remove(r);
55767                                         this.store.insert(insertAt++, r);
55768                                 }
55769                                 this.isDirtyFlag = true;
55770                         }
55771                 }
55772                 this.dragZone.cachedTarget = null;
55773                 return true;
55774     },
55775
55776     removeDropIndicators : function(n){
55777                 if(n){
55778                         Roo.fly(n).removeClass([
55779                                 "x-view-drag-insert-above",
55780                                 "x-view-drag-insert-below"]);
55781                         this.lastInsertClass = "_noclass";
55782                 }
55783     },
55784
55785 /**
55786  *      Utility method. Add a delete option to the DDView's context menu.
55787  *      @param {String} imageUrl The URL of the "delete" icon image.
55788  */
55789         setDeletable: function(imageUrl) {
55790                 if (!this.singleSelect && !this.multiSelect) {
55791                         this.singleSelect = true;
55792                 }
55793                 var c = this.getContextMenu();
55794                 this.contextMenu.on("itemclick", function(item) {
55795                         switch (item.id) {
55796                                 case "delete":
55797                                         this.remove(this.getSelectedIndexes());
55798                                         break;
55799                         }
55800                 }, this);
55801                 this.contextMenu.add({
55802                         icon: imageUrl,
55803                         id: "delete",
55804                         text: 'Delete'
55805                 });
55806         },
55807         
55808 /**     Return the context menu for this DDView. */
55809         getContextMenu: function() {
55810                 if (!this.contextMenu) {
55811 //                      Create the View's context menu
55812                         this.contextMenu = new Roo.menu.Menu({
55813                                 id: this.id + "-contextmenu"
55814                         });
55815                         this.el.on("contextmenu", this.showContextMenu, this);
55816                 }
55817                 return this.contextMenu;
55818         },
55819         
55820         disableContextMenu: function() {
55821                 if (this.contextMenu) {
55822                         this.el.un("contextmenu", this.showContextMenu, this);
55823                 }
55824         },
55825
55826         showContextMenu: function(e, item) {
55827         item = this.findItemFromChild(e.getTarget());
55828                 if (item) {
55829                         e.stopEvent();
55830                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
55831                         this.contextMenu.showAt(e.getXY());
55832             }
55833     },
55834
55835 /**
55836  *      Remove {@link Roo.data.Record}s at the specified indices.
55837  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
55838  */
55839     remove: function(selectedIndices) {
55840                 selectedIndices = [].concat(selectedIndices);
55841                 for (var i = 0; i < selectedIndices.length; i++) {
55842                         var rec = this.store.getAt(selectedIndices[i]);
55843                         this.store.remove(rec);
55844                 }
55845     },
55846
55847 /**
55848  *      Double click fires the event, but also, if this is draggable, and there is only one other
55849  *      related DropZone, it transfers the selected node.
55850  */
55851     onDblClick : function(e){
55852         var item = this.findItemFromChild(e.getTarget());
55853         if(item){
55854             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
55855                 return false;
55856             }
55857             if (this.dragGroup) {
55858                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
55859                     while (targets.indexOf(this.dropZone) > -1) {
55860                             targets.remove(this.dropZone);
55861                                 }
55862                     if (targets.length == 1) {
55863                                         this.dragZone.cachedTarget = null;
55864                         var el = Roo.get(targets[0].getEl());
55865                         var box = el.getBox(true);
55866                         targets[0].onNodeDrop(el.dom, {
55867                                 target: el.dom,
55868                                 xy: [box.x, box.y + box.height - 1]
55869                         }, null, this.getDragData(e));
55870                     }
55871                 }
55872         }
55873     },
55874     
55875     handleSelection: function(e) {
55876                 this.dragZone.cachedTarget = null;
55877         var item = this.findItemFromChild(e.getTarget());
55878         if (!item) {
55879                 this.clearSelections(true);
55880                 return;
55881         }
55882                 if (item && (this.multiSelect || this.singleSelect)){
55883                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
55884                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
55885                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
55886                                 this.unselect(item);
55887                         } else {
55888                                 this.select(item, this.multiSelect && e.ctrlKey);
55889                                 this.lastSelection = item;
55890                         }
55891                 }
55892     },
55893
55894     onItemClick : function(item, index, e){
55895                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
55896                         return false;
55897                 }
55898                 return true;
55899     },
55900
55901     unselect : function(nodeInfo, suppressEvent){
55902                 var node = this.getNode(nodeInfo);
55903                 if(node && this.isSelected(node)){
55904                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
55905                                 Roo.fly(node).removeClass(this.selectedClass);
55906                                 this.selections.remove(node);
55907                                 if(!suppressEvent){
55908                                         this.fireEvent("selectionchange", this, this.selections);
55909                                 }
55910                         }
55911                 }
55912     }
55913 });
55914 /*
55915  * Based on:
55916  * Ext JS Library 1.1.1
55917  * Copyright(c) 2006-2007, Ext JS, LLC.
55918  *
55919  * Originally Released Under LGPL - original licence link has changed is not relivant.
55920  *
55921  * Fork - LGPL
55922  * <script type="text/javascript">
55923  */
55924  
55925 /**
55926  * @class Roo.LayoutManager
55927  * @extends Roo.util.Observable
55928  * Base class for layout managers.
55929  */
55930 Roo.LayoutManager = function(container, config){
55931     Roo.LayoutManager.superclass.constructor.call(this);
55932     this.el = Roo.get(container);
55933     // ie scrollbar fix
55934     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
55935         document.body.scroll = "no";
55936     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
55937         this.el.position('relative');
55938     }
55939     this.id = this.el.id;
55940     this.el.addClass("x-layout-container");
55941     /** false to disable window resize monitoring @type Boolean */
55942     this.monitorWindowResize = true;
55943     this.regions = {};
55944     this.addEvents({
55945         /**
55946          * @event layout
55947          * Fires when a layout is performed. 
55948          * @param {Roo.LayoutManager} this
55949          */
55950         "layout" : true,
55951         /**
55952          * @event regionresized
55953          * Fires when the user resizes a region. 
55954          * @param {Roo.LayoutRegion} region The resized region
55955          * @param {Number} newSize The new size (width for east/west, height for north/south)
55956          */
55957         "regionresized" : true,
55958         /**
55959          * @event regioncollapsed
55960          * Fires when a region is collapsed. 
55961          * @param {Roo.LayoutRegion} region The collapsed region
55962          */
55963         "regioncollapsed" : true,
55964         /**
55965          * @event regionexpanded
55966          * Fires when a region is expanded.  
55967          * @param {Roo.LayoutRegion} region The expanded region
55968          */
55969         "regionexpanded" : true
55970     });
55971     this.updating = false;
55972     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
55973 };
55974
55975 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
55976     /**
55977      * Returns true if this layout is currently being updated
55978      * @return {Boolean}
55979      */
55980     isUpdating : function(){
55981         return this.updating; 
55982     },
55983     
55984     /**
55985      * Suspend the LayoutManager from doing auto-layouts while
55986      * making multiple add or remove calls
55987      */
55988     beginUpdate : function(){
55989         this.updating = true;    
55990     },
55991     
55992     /**
55993      * Restore auto-layouts and optionally disable the manager from performing a layout
55994      * @param {Boolean} noLayout true to disable a layout update 
55995      */
55996     endUpdate : function(noLayout){
55997         this.updating = false;
55998         if(!noLayout){
55999             this.layout();
56000         }    
56001     },
56002     
56003     layout: function(){
56004         
56005     },
56006     
56007     onRegionResized : function(region, newSize){
56008         this.fireEvent("regionresized", region, newSize);
56009         this.layout();
56010     },
56011     
56012     onRegionCollapsed : function(region){
56013         this.fireEvent("regioncollapsed", region);
56014     },
56015     
56016     onRegionExpanded : function(region){
56017         this.fireEvent("regionexpanded", region);
56018     },
56019         
56020     /**
56021      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
56022      * performs box-model adjustments.
56023      * @return {Object} The size as an object {width: (the width), height: (the height)}
56024      */
56025     getViewSize : function(){
56026         var size;
56027         if(this.el.dom != document.body){
56028             size = this.el.getSize();
56029         }else{
56030             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
56031         }
56032         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
56033         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
56034         return size;
56035     },
56036     
56037     /**
56038      * Returns the Element this layout is bound to.
56039      * @return {Roo.Element}
56040      */
56041     getEl : function(){
56042         return this.el;
56043     },
56044     
56045     /**
56046      * Returns the specified region.
56047      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
56048      * @return {Roo.LayoutRegion}
56049      */
56050     getRegion : function(target){
56051         return this.regions[target.toLowerCase()];
56052     },
56053     
56054     onWindowResize : function(){
56055         if(this.monitorWindowResize){
56056             this.layout();
56057         }
56058     }
56059 });/*
56060  * Based on:
56061  * Ext JS Library 1.1.1
56062  * Copyright(c) 2006-2007, Ext JS, LLC.
56063  *
56064  * Originally Released Under LGPL - original licence link has changed is not relivant.
56065  *
56066  * Fork - LGPL
56067  * <script type="text/javascript">
56068  */
56069 /**
56070  * @class Roo.BorderLayout
56071  * @extends Roo.LayoutManager
56072  * @children Roo.ContentPanel
56073  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
56074  * please see: <br><br>
56075  * <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>
56076  * <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>
56077  * Example:
56078  <pre><code>
56079  var layout = new Roo.BorderLayout(document.body, {
56080     north: {
56081         initialSize: 25,
56082         titlebar: false
56083     },
56084     west: {
56085         split:true,
56086         initialSize: 200,
56087         minSize: 175,
56088         maxSize: 400,
56089         titlebar: true,
56090         collapsible: true
56091     },
56092     east: {
56093         split:true,
56094         initialSize: 202,
56095         minSize: 175,
56096         maxSize: 400,
56097         titlebar: true,
56098         collapsible: true
56099     },
56100     south: {
56101         split:true,
56102         initialSize: 100,
56103         minSize: 100,
56104         maxSize: 200,
56105         titlebar: true,
56106         collapsible: true
56107     },
56108     center: {
56109         titlebar: true,
56110         autoScroll:true,
56111         resizeTabs: true,
56112         minTabWidth: 50,
56113         preferredTabWidth: 150
56114     }
56115 });
56116
56117 // shorthand
56118 var CP = Roo.ContentPanel;
56119
56120 layout.beginUpdate();
56121 layout.add("north", new CP("north", "North"));
56122 layout.add("south", new CP("south", {title: "South", closable: true}));
56123 layout.add("west", new CP("west", {title: "West"}));
56124 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
56125 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
56126 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
56127 layout.getRegion("center").showPanel("center1");
56128 layout.endUpdate();
56129 </code></pre>
56130
56131 <b>The container the layout is rendered into can be either the body element or any other element.
56132 If it is not the body element, the container needs to either be an absolute positioned element,
56133 or you will need to add "position:relative" to the css of the container.  You will also need to specify
56134 the container size if it is not the body element.</b>
56135
56136 * @constructor
56137 * Create a new BorderLayout
56138 * @param {String/HTMLElement/Element} container The container this layout is bound to
56139 * @param {Object} config Configuration options
56140  */
56141 Roo.BorderLayout = function(container, config){
56142     config = config || {};
56143     Roo.BorderLayout.superclass.constructor.call(this, container, config);
56144     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
56145     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
56146         var target = this.factory.validRegions[i];
56147         if(config[target]){
56148             this.addRegion(target, config[target]);
56149         }
56150     }
56151 };
56152
56153 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
56154         
56155         /**
56156          * @cfg {Roo.LayoutRegion} east
56157          */
56158         /**
56159          * @cfg {Roo.LayoutRegion} west
56160          */
56161         /**
56162          * @cfg {Roo.LayoutRegion} north
56163          */
56164         /**
56165          * @cfg {Roo.LayoutRegion} south
56166          */
56167         /**
56168          * @cfg {Roo.LayoutRegion} center
56169          */
56170     /**
56171      * Creates and adds a new region if it doesn't already exist.
56172      * @param {String} target The target region key (north, south, east, west or center).
56173      * @param {Object} config The regions config object
56174      * @return {BorderLayoutRegion} The new region
56175      */
56176     addRegion : function(target, config){
56177         if(!this.regions[target]){
56178             var r = this.factory.create(target, this, config);
56179             this.bindRegion(target, r);
56180         }
56181         return this.regions[target];
56182     },
56183
56184     // private (kinda)
56185     bindRegion : function(name, r){
56186         this.regions[name] = r;
56187         r.on("visibilitychange", this.layout, this);
56188         r.on("paneladded", this.layout, this);
56189         r.on("panelremoved", this.layout, this);
56190         r.on("invalidated", this.layout, this);
56191         r.on("resized", this.onRegionResized, this);
56192         r.on("collapsed", this.onRegionCollapsed, this);
56193         r.on("expanded", this.onRegionExpanded, this);
56194     },
56195
56196     /**
56197      * Performs a layout update.
56198      */
56199     layout : function(){
56200         if(this.updating) {
56201             return;
56202         }
56203         var size = this.getViewSize();
56204         var w = size.width;
56205         var h = size.height;
56206         var centerW = w;
56207         var centerH = h;
56208         var centerY = 0;
56209         var centerX = 0;
56210         //var x = 0, y = 0;
56211
56212         var rs = this.regions;
56213         var north = rs["north"];
56214         var south = rs["south"]; 
56215         var west = rs["west"];
56216         var east = rs["east"];
56217         var center = rs["center"];
56218         //if(this.hideOnLayout){ // not supported anymore
56219             //c.el.setStyle("display", "none");
56220         //}
56221         if(north && north.isVisible()){
56222             var b = north.getBox();
56223             var m = north.getMargins();
56224             b.width = w - (m.left+m.right);
56225             b.x = m.left;
56226             b.y = m.top;
56227             centerY = b.height + b.y + m.bottom;
56228             centerH -= centerY;
56229             north.updateBox(this.safeBox(b));
56230         }
56231         if(south && south.isVisible()){
56232             var b = south.getBox();
56233             var m = south.getMargins();
56234             b.width = w - (m.left+m.right);
56235             b.x = m.left;
56236             var totalHeight = (b.height + m.top + m.bottom);
56237             b.y = h - totalHeight + m.top;
56238             centerH -= totalHeight;
56239             south.updateBox(this.safeBox(b));
56240         }
56241         if(west && west.isVisible()){
56242             var b = west.getBox();
56243             var m = west.getMargins();
56244             b.height = centerH - (m.top+m.bottom);
56245             b.x = m.left;
56246             b.y = centerY + m.top;
56247             var totalWidth = (b.width + m.left + m.right);
56248             centerX += totalWidth;
56249             centerW -= totalWidth;
56250             west.updateBox(this.safeBox(b));
56251         }
56252         if(east && east.isVisible()){
56253             var b = east.getBox();
56254             var m = east.getMargins();
56255             b.height = centerH - (m.top+m.bottom);
56256             var totalWidth = (b.width + m.left + m.right);
56257             b.x = w - totalWidth + m.left;
56258             b.y = centerY + m.top;
56259             centerW -= totalWidth;
56260             east.updateBox(this.safeBox(b));
56261         }
56262         if(center){
56263             var m = center.getMargins();
56264             var centerBox = {
56265                 x: centerX + m.left,
56266                 y: centerY + m.top,
56267                 width: centerW - (m.left+m.right),
56268                 height: centerH - (m.top+m.bottom)
56269             };
56270             //if(this.hideOnLayout){
56271                 //center.el.setStyle("display", "block");
56272             //}
56273             center.updateBox(this.safeBox(centerBox));
56274         }
56275         this.el.repaint();
56276         this.fireEvent("layout", this);
56277     },
56278
56279     // private
56280     safeBox : function(box){
56281         box.width = Math.max(0, box.width);
56282         box.height = Math.max(0, box.height);
56283         return box;
56284     },
56285
56286     /**
56287      * Adds a ContentPanel (or subclass) to this layout.
56288      * @param {String} target The target region key (north, south, east, west or center).
56289      * @param {Roo.ContentPanel} panel The panel to add
56290      * @return {Roo.ContentPanel} The added panel
56291      */
56292     add : function(target, panel){
56293          
56294         target = target.toLowerCase();
56295         return this.regions[target].add(panel);
56296     },
56297
56298     /**
56299      * Remove a ContentPanel (or subclass) to this layout.
56300      * @param {String} target The target region key (north, south, east, west or center).
56301      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
56302      * @return {Roo.ContentPanel} The removed panel
56303      */
56304     remove : function(target, panel){
56305         target = target.toLowerCase();
56306         return this.regions[target].remove(panel);
56307     },
56308
56309     /**
56310      * Searches all regions for a panel with the specified id
56311      * @param {String} panelId
56312      * @return {Roo.ContentPanel} The panel or null if it wasn't found
56313      */
56314     findPanel : function(panelId){
56315         var rs = this.regions;
56316         for(var target in rs){
56317             if(typeof rs[target] != "function"){
56318                 var p = rs[target].getPanel(panelId);
56319                 if(p){
56320                     return p;
56321                 }
56322             }
56323         }
56324         return null;
56325     },
56326
56327     /**
56328      * Searches all regions for a panel with the specified id and activates (shows) it.
56329      * @param {String/ContentPanel} panelId The panels id or the panel itself
56330      * @return {Roo.ContentPanel} The shown panel or null
56331      */
56332     showPanel : function(panelId) {
56333       var rs = this.regions;
56334       for(var target in rs){
56335          var r = rs[target];
56336          if(typeof r != "function"){
56337             if(r.hasPanel(panelId)){
56338                return r.showPanel(panelId);
56339             }
56340          }
56341       }
56342       return null;
56343    },
56344
56345    /**
56346      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
56347      * @param {Roo.state.Provider} provider (optional) An alternate state provider
56348      */
56349     restoreState : function(provider){
56350         if(!provider){
56351             provider = Roo.state.Manager;
56352         }
56353         var sm = new Roo.LayoutStateManager();
56354         sm.init(this, provider);
56355     },
56356
56357     /**
56358      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
56359      * object should contain properties for each region to add ContentPanels to, and each property's value should be
56360      * a valid ContentPanel config object.  Example:
56361      * <pre><code>
56362 // Create the main layout
56363 var layout = new Roo.BorderLayout('main-ct', {
56364     west: {
56365         split:true,
56366         minSize: 175,
56367         titlebar: true
56368     },
56369     center: {
56370         title:'Components'
56371     }
56372 }, 'main-ct');
56373
56374 // Create and add multiple ContentPanels at once via configs
56375 layout.batchAdd({
56376    west: {
56377        id: 'source-files',
56378        autoCreate:true,
56379        title:'Ext Source Files',
56380        autoScroll:true,
56381        fitToFrame:true
56382    },
56383    center : {
56384        el: cview,
56385        autoScroll:true,
56386        fitToFrame:true,
56387        toolbar: tb,
56388        resizeEl:'cbody'
56389    }
56390 });
56391 </code></pre>
56392      * @param {Object} regions An object containing ContentPanel configs by region name
56393      */
56394     batchAdd : function(regions){
56395         this.beginUpdate();
56396         for(var rname in regions){
56397             var lr = this.regions[rname];
56398             if(lr){
56399                 this.addTypedPanels(lr, regions[rname]);
56400             }
56401         }
56402         this.endUpdate();
56403     },
56404
56405     // private
56406     addTypedPanels : function(lr, ps){
56407         if(typeof ps == 'string'){
56408             lr.add(new Roo.ContentPanel(ps));
56409         }
56410         else if(ps instanceof Array){
56411             for(var i =0, len = ps.length; i < len; i++){
56412                 this.addTypedPanels(lr, ps[i]);
56413             }
56414         }
56415         else if(!ps.events){ // raw config?
56416             var el = ps.el;
56417             delete ps.el; // prevent conflict
56418             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
56419         }
56420         else {  // panel object assumed!
56421             lr.add(ps);
56422         }
56423     },
56424     /**
56425      * Adds a xtype elements to the layout.
56426      * <pre><code>
56427
56428 layout.addxtype({
56429        xtype : 'ContentPanel',
56430        region: 'west',
56431        items: [ .... ]
56432    }
56433 );
56434
56435 layout.addxtype({
56436         xtype : 'NestedLayoutPanel',
56437         region: 'west',
56438         layout: {
56439            center: { },
56440            west: { }   
56441         },
56442         items : [ ... list of content panels or nested layout panels.. ]
56443    }
56444 );
56445 </code></pre>
56446      * @param {Object} cfg Xtype definition of item to add.
56447      */
56448     addxtype : function(cfg)
56449     {
56450         // basically accepts a pannel...
56451         // can accept a layout region..!?!?
56452         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
56453         
56454         if (!cfg.xtype.match(/Panel$/)) {
56455             return false;
56456         }
56457         var ret = false;
56458         
56459         if (typeof(cfg.region) == 'undefined') {
56460             Roo.log("Failed to add Panel, region was not set");
56461             Roo.log(cfg);
56462             return false;
56463         }
56464         var region = cfg.region;
56465         delete cfg.region;
56466         
56467           
56468         var xitems = [];
56469         if (cfg.items) {
56470             xitems = cfg.items;
56471             delete cfg.items;
56472         }
56473         var nb = false;
56474         
56475         switch(cfg.xtype) 
56476         {
56477             case 'ContentPanel':  // ContentPanel (el, cfg)
56478             case 'ScrollPanel':  // ContentPanel (el, cfg)
56479             case 'ViewPanel': 
56480                 if(cfg.autoCreate) {
56481                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
56482                 } else {
56483                     var el = this.el.createChild();
56484                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
56485                 }
56486                 
56487                 this.add(region, ret);
56488                 break;
56489             
56490             
56491             case 'TreePanel': // our new panel!
56492                 cfg.el = this.el.createChild();
56493                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
56494                 this.add(region, ret);
56495                 break;
56496             
56497             case 'NestedLayoutPanel': 
56498                 // create a new Layout (which is  a Border Layout...
56499                 var el = this.el.createChild();
56500                 var clayout = cfg.layout;
56501                 delete cfg.layout;
56502                 clayout.items   = clayout.items  || [];
56503                 // replace this exitems with the clayout ones..
56504                 xitems = clayout.items;
56505                  
56506                 
56507                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
56508                     cfg.background = false;
56509                 }
56510                 var layout = new Roo.BorderLayout(el, clayout);
56511                 
56512                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
56513                 //console.log('adding nested layout panel '  + cfg.toSource());
56514                 this.add(region, ret);
56515                 nb = {}; /// find first...
56516                 break;
56517                 
56518             case 'GridPanel': 
56519             
56520                 // needs grid and region
56521                 
56522                 //var el = this.getRegion(region).el.createChild();
56523                 var el = this.el.createChild();
56524                 // create the grid first...
56525                 
56526                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
56527                 delete cfg.grid;
56528                 if (region == 'center' && this.active ) {
56529                     cfg.background = false;
56530                 }
56531                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
56532                 
56533                 this.add(region, ret);
56534                 if (cfg.background) {
56535                     ret.on('activate', function(gp) {
56536                         if (!gp.grid.rendered) {
56537                             gp.grid.render();
56538                         }
56539                     });
56540                 } else {
56541                     grid.render();
56542                 }
56543                 break;
56544            
56545            
56546            
56547                 
56548                 
56549                 
56550             default:
56551                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
56552                     
56553                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
56554                     this.add(region, ret);
56555                 } else {
56556                 
56557                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
56558                     return null;
56559                 }
56560                 
56561              // GridPanel (grid, cfg)
56562             
56563         }
56564         this.beginUpdate();
56565         // add children..
56566         var region = '';
56567         var abn = {};
56568         Roo.each(xitems, function(i)  {
56569             region = nb && i.region ? i.region : false;
56570             
56571             var add = ret.addxtype(i);
56572            
56573             if (region) {
56574                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
56575                 if (!i.background) {
56576                     abn[region] = nb[region] ;
56577                 }
56578             }
56579             
56580         });
56581         this.endUpdate();
56582
56583         // make the last non-background panel active..
56584         //if (nb) { Roo.log(abn); }
56585         if (nb) {
56586             
56587             for(var r in abn) {
56588                 region = this.getRegion(r);
56589                 if (region) {
56590                     // tried using nb[r], but it does not work..
56591                      
56592                     region.showPanel(abn[r]);
56593                    
56594                 }
56595             }
56596         }
56597         return ret;
56598         
56599     }
56600 });
56601
56602 /**
56603  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
56604  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
56605  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
56606  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
56607  * <pre><code>
56608 // shorthand
56609 var CP = Roo.ContentPanel;
56610
56611 var layout = Roo.BorderLayout.create({
56612     north: {
56613         initialSize: 25,
56614         titlebar: false,
56615         panels: [new CP("north", "North")]
56616     },
56617     west: {
56618         split:true,
56619         initialSize: 200,
56620         minSize: 175,
56621         maxSize: 400,
56622         titlebar: true,
56623         collapsible: true,
56624         panels: [new CP("west", {title: "West"})]
56625     },
56626     east: {
56627         split:true,
56628         initialSize: 202,
56629         minSize: 175,
56630         maxSize: 400,
56631         titlebar: true,
56632         collapsible: true,
56633         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
56634     },
56635     south: {
56636         split:true,
56637         initialSize: 100,
56638         minSize: 100,
56639         maxSize: 200,
56640         titlebar: true,
56641         collapsible: true,
56642         panels: [new CP("south", {title: "South", closable: true})]
56643     },
56644     center: {
56645         titlebar: true,
56646         autoScroll:true,
56647         resizeTabs: true,
56648         minTabWidth: 50,
56649         preferredTabWidth: 150,
56650         panels: [
56651             new CP("center1", {title: "Close Me", closable: true}),
56652             new CP("center2", {title: "Center Panel", closable: false})
56653         ]
56654     }
56655 }, document.body);
56656
56657 layout.getRegion("center").showPanel("center1");
56658 </code></pre>
56659  * @param config
56660  * @param targetEl
56661  */
56662 Roo.BorderLayout.create = function(config, targetEl){
56663     var layout = new Roo.BorderLayout(targetEl || document.body, config);
56664     layout.beginUpdate();
56665     var regions = Roo.BorderLayout.RegionFactory.validRegions;
56666     for(var j = 0, jlen = regions.length; j < jlen; j++){
56667         var lr = regions[j];
56668         if(layout.regions[lr] && config[lr].panels){
56669             var r = layout.regions[lr];
56670             var ps = config[lr].panels;
56671             layout.addTypedPanels(r, ps);
56672         }
56673     }
56674     layout.endUpdate();
56675     return layout;
56676 };
56677
56678 // private
56679 Roo.BorderLayout.RegionFactory = {
56680     // private
56681     validRegions : ["north","south","east","west","center"],
56682
56683     // private
56684     create : function(target, mgr, config){
56685         target = target.toLowerCase();
56686         if(config.lightweight || config.basic){
56687             return new Roo.BasicLayoutRegion(mgr, config, target);
56688         }
56689         switch(target){
56690             case "north":
56691                 return new Roo.NorthLayoutRegion(mgr, config);
56692             case "south":
56693                 return new Roo.SouthLayoutRegion(mgr, config);
56694             case "east":
56695                 return new Roo.EastLayoutRegion(mgr, config);
56696             case "west":
56697                 return new Roo.WestLayoutRegion(mgr, config);
56698             case "center":
56699                 return new Roo.CenterLayoutRegion(mgr, config);
56700         }
56701         throw 'Layout region "'+target+'" not supported.';
56702     }
56703 };/*
56704  * Based on:
56705  * Ext JS Library 1.1.1
56706  * Copyright(c) 2006-2007, Ext JS, LLC.
56707  *
56708  * Originally Released Under LGPL - original licence link has changed is not relivant.
56709  *
56710  * Fork - LGPL
56711  * <script type="text/javascript">
56712  */
56713  
56714 /**
56715  * @class Roo.BasicLayoutRegion
56716  * @extends Roo.util.Observable
56717  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
56718  * and does not have a titlebar, tabs or any other features. All it does is size and position 
56719  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
56720  */
56721 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
56722     this.mgr = mgr;
56723     this.position  = pos;
56724     this.events = {
56725         /**
56726          * @scope Roo.BasicLayoutRegion
56727          */
56728         
56729         /**
56730          * @event beforeremove
56731          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
56732          * @param {Roo.LayoutRegion} this
56733          * @param {Roo.ContentPanel} panel The panel
56734          * @param {Object} e The cancel event object
56735          */
56736         "beforeremove" : true,
56737         /**
56738          * @event invalidated
56739          * Fires when the layout for this region is changed.
56740          * @param {Roo.LayoutRegion} this
56741          */
56742         "invalidated" : true,
56743         /**
56744          * @event visibilitychange
56745          * Fires when this region is shown or hidden 
56746          * @param {Roo.LayoutRegion} this
56747          * @param {Boolean} visibility true or false
56748          */
56749         "visibilitychange" : true,
56750         /**
56751          * @event paneladded
56752          * Fires when a panel is added. 
56753          * @param {Roo.LayoutRegion} this
56754          * @param {Roo.ContentPanel} panel The panel
56755          */
56756         "paneladded" : true,
56757         /**
56758          * @event panelremoved
56759          * Fires when a panel is removed. 
56760          * @param {Roo.LayoutRegion} this
56761          * @param {Roo.ContentPanel} panel The panel
56762          */
56763         "panelremoved" : true,
56764         /**
56765          * @event beforecollapse
56766          * Fires when this region before collapse.
56767          * @param {Roo.LayoutRegion} this
56768          */
56769         "beforecollapse" : true,
56770         /**
56771          * @event collapsed
56772          * Fires when this region is collapsed.
56773          * @param {Roo.LayoutRegion} this
56774          */
56775         "collapsed" : true,
56776         /**
56777          * @event expanded
56778          * Fires when this region is expanded.
56779          * @param {Roo.LayoutRegion} this
56780          */
56781         "expanded" : true,
56782         /**
56783          * @event slideshow
56784          * Fires when this region is slid into view.
56785          * @param {Roo.LayoutRegion} this
56786          */
56787         "slideshow" : true,
56788         /**
56789          * @event slidehide
56790          * Fires when this region slides out of view. 
56791          * @param {Roo.LayoutRegion} this
56792          */
56793         "slidehide" : true,
56794         /**
56795          * @event panelactivated
56796          * Fires when a panel is activated. 
56797          * @param {Roo.LayoutRegion} this
56798          * @param {Roo.ContentPanel} panel The activated panel
56799          */
56800         "panelactivated" : true,
56801         /**
56802          * @event resized
56803          * Fires when the user resizes this region. 
56804          * @param {Roo.LayoutRegion} this
56805          * @param {Number} newSize The new size (width for east/west, height for north/south)
56806          */
56807         "resized" : true
56808     };
56809     /** A collection of panels in this region. @type Roo.util.MixedCollection */
56810     this.panels = new Roo.util.MixedCollection();
56811     this.panels.getKey = this.getPanelId.createDelegate(this);
56812     this.box = null;
56813     this.activePanel = null;
56814     // ensure listeners are added...
56815     
56816     if (config.listeners || config.events) {
56817         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
56818             listeners : config.listeners || {},
56819             events : config.events || {}
56820         });
56821     }
56822     
56823     if(skipConfig !== true){
56824         this.applyConfig(config);
56825     }
56826 };
56827
56828 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
56829     getPanelId : function(p){
56830         return p.getId();
56831     },
56832     
56833     applyConfig : function(config){
56834         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
56835         this.config = config;
56836         
56837     },
56838     
56839     /**
56840      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
56841      * the width, for horizontal (north, south) the height.
56842      * @param {Number} newSize The new width or height
56843      */
56844     resizeTo : function(newSize){
56845         var el = this.el ? this.el :
56846                  (this.activePanel ? this.activePanel.getEl() : null);
56847         if(el){
56848             switch(this.position){
56849                 case "east":
56850                 case "west":
56851                     el.setWidth(newSize);
56852                     this.fireEvent("resized", this, newSize);
56853                 break;
56854                 case "north":
56855                 case "south":
56856                     el.setHeight(newSize);
56857                     this.fireEvent("resized", this, newSize);
56858                 break;                
56859             }
56860         }
56861     },
56862     
56863     getBox : function(){
56864         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
56865     },
56866     
56867     getMargins : function(){
56868         return this.margins;
56869     },
56870     
56871     updateBox : function(box){
56872         this.box = box;
56873         var el = this.activePanel.getEl();
56874         el.dom.style.left = box.x + "px";
56875         el.dom.style.top = box.y + "px";
56876         this.activePanel.setSize(box.width, box.height);
56877     },
56878     
56879     /**
56880      * Returns the container element for this region.
56881      * @return {Roo.Element}
56882      */
56883     getEl : function(){
56884         return this.activePanel;
56885     },
56886     
56887     /**
56888      * Returns true if this region is currently visible.
56889      * @return {Boolean}
56890      */
56891     isVisible : function(){
56892         return this.activePanel ? true : false;
56893     },
56894     
56895     setActivePanel : function(panel){
56896         panel = this.getPanel(panel);
56897         if(this.activePanel && this.activePanel != panel){
56898             this.activePanel.setActiveState(false);
56899             this.activePanel.getEl().setLeftTop(-10000,-10000);
56900         }
56901         this.activePanel = panel;
56902         panel.setActiveState(true);
56903         if(this.box){
56904             panel.setSize(this.box.width, this.box.height);
56905         }
56906         this.fireEvent("panelactivated", this, panel);
56907         this.fireEvent("invalidated");
56908     },
56909     
56910     /**
56911      * Show the specified panel.
56912      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
56913      * @return {Roo.ContentPanel} The shown panel or null
56914      */
56915     showPanel : function(panel){
56916         if(panel = this.getPanel(panel)){
56917             this.setActivePanel(panel);
56918         }
56919         return panel;
56920     },
56921     
56922     /**
56923      * Get the active panel for this region.
56924      * @return {Roo.ContentPanel} The active panel or null
56925      */
56926     getActivePanel : function(){
56927         return this.activePanel;
56928     },
56929     
56930     /**
56931      * Add the passed ContentPanel(s)
56932      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
56933      * @return {Roo.ContentPanel} The panel added (if only one was added)
56934      */
56935     add : function(panel){
56936         if(arguments.length > 1){
56937             for(var i = 0, len = arguments.length; i < len; i++) {
56938                 this.add(arguments[i]);
56939             }
56940             return null;
56941         }
56942         if(this.hasPanel(panel)){
56943             this.showPanel(panel);
56944             return panel;
56945         }
56946         var el = panel.getEl();
56947         if(el.dom.parentNode != this.mgr.el.dom){
56948             this.mgr.el.dom.appendChild(el.dom);
56949         }
56950         if(panel.setRegion){
56951             panel.setRegion(this);
56952         }
56953         this.panels.add(panel);
56954         el.setStyle("position", "absolute");
56955         if(!panel.background){
56956             this.setActivePanel(panel);
56957             if(this.config.initialSize && this.panels.getCount()==1){
56958                 this.resizeTo(this.config.initialSize);
56959             }
56960         }
56961         this.fireEvent("paneladded", this, panel);
56962         return panel;
56963     },
56964     
56965     /**
56966      * Returns true if the panel is in this region.
56967      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
56968      * @return {Boolean}
56969      */
56970     hasPanel : function(panel){
56971         if(typeof panel == "object"){ // must be panel obj
56972             panel = panel.getId();
56973         }
56974         return this.getPanel(panel) ? true : false;
56975     },
56976     
56977     /**
56978      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
56979      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
56980      * @param {Boolean} preservePanel Overrides the config preservePanel option
56981      * @return {Roo.ContentPanel} The panel that was removed
56982      */
56983     remove : function(panel, preservePanel){
56984         panel = this.getPanel(panel);
56985         if(!panel){
56986             return null;
56987         }
56988         var e = {};
56989         this.fireEvent("beforeremove", this, panel, e);
56990         if(e.cancel === true){
56991             return null;
56992         }
56993         var panelId = panel.getId();
56994         this.panels.removeKey(panelId);
56995         return panel;
56996     },
56997     
56998     /**
56999      * Returns the panel specified or null if it's not in this region.
57000      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
57001      * @return {Roo.ContentPanel}
57002      */
57003     getPanel : function(id){
57004         if(typeof id == "object"){ // must be panel obj
57005             return id;
57006         }
57007         return this.panels.get(id);
57008     },
57009     
57010     /**
57011      * Returns this regions position (north/south/east/west/center).
57012      * @return {String} 
57013      */
57014     getPosition: function(){
57015         return this.position;    
57016     }
57017 });/*
57018  * Based on:
57019  * Ext JS Library 1.1.1
57020  * Copyright(c) 2006-2007, Ext JS, LLC.
57021  *
57022  * Originally Released Under LGPL - original licence link has changed is not relivant.
57023  *
57024  * Fork - LGPL
57025  * <script type="text/javascript">
57026  */
57027  
57028 /**
57029  * @class Roo.LayoutRegion
57030  * @extends Roo.BasicLayoutRegion
57031  * This class represents a region in a layout manager.
57032  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
57033  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
57034  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
57035  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
57036  * @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})
57037  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
57038  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
57039  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
57040  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
57041  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
57042  * @cfg {String}    title           The title for the region (overrides panel titles)
57043  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
57044  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
57045  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
57046  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
57047  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
57048  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
57049  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
57050  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
57051  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
57052  * @cfg {Boolean}   showPin         True to show a pin button
57053  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
57054  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
57055  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
57056  * @cfg {Number}    width           For East/West panels
57057  * @cfg {Number}    height          For North/South panels
57058  * @cfg {Boolean}   split           To show the splitter
57059  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
57060  */
57061 Roo.LayoutRegion = function(mgr, config, pos){
57062     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
57063     var dh = Roo.DomHelper;
57064     /** This region's container element 
57065     * @type Roo.Element */
57066     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
57067     /** This region's title element 
57068     * @type Roo.Element */
57069
57070     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
57071         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
57072         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
57073     ]}, true);
57074     this.titleEl.enableDisplayMode();
57075     /** This region's title text element 
57076     * @type HTMLElement */
57077     this.titleTextEl = this.titleEl.dom.firstChild;
57078     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
57079     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
57080     this.closeBtn.enableDisplayMode();
57081     this.closeBtn.on("click", this.closeClicked, this);
57082     this.closeBtn.hide();
57083
57084     this.createBody(config);
57085     this.visible = true;
57086     this.collapsed = false;
57087
57088     if(config.hideWhenEmpty){
57089         this.hide();
57090         this.on("paneladded", this.validateVisibility, this);
57091         this.on("panelremoved", this.validateVisibility, this);
57092     }
57093     this.applyConfig(config);
57094 };
57095
57096 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
57097
57098     createBody : function(){
57099         /** This region's body element 
57100         * @type Roo.Element */
57101         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
57102     },
57103
57104     applyConfig : function(c){
57105         if(c.collapsible && this.position != "center" && !this.collapsedEl){
57106             var dh = Roo.DomHelper;
57107             if(c.titlebar !== false){
57108                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
57109                 this.collapseBtn.on("click", this.collapse, this);
57110                 this.collapseBtn.enableDisplayMode();
57111
57112                 if(c.showPin === true || this.showPin){
57113                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
57114                     this.stickBtn.enableDisplayMode();
57115                     this.stickBtn.on("click", this.expand, this);
57116                     this.stickBtn.hide();
57117                 }
57118             }
57119             /** This region's collapsed element
57120             * @type Roo.Element */
57121             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
57122                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
57123             ]}, true);
57124             if(c.floatable !== false){
57125                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
57126                this.collapsedEl.on("click", this.collapseClick, this);
57127             }
57128
57129             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
57130                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
57131                    id: "message", unselectable: "on", style:{"float":"left"}});
57132                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
57133              }
57134             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
57135             this.expandBtn.on("click", this.expand, this);
57136         }
57137         if(this.collapseBtn){
57138             this.collapseBtn.setVisible(c.collapsible == true);
57139         }
57140         this.cmargins = c.cmargins || this.cmargins ||
57141                          (this.position == "west" || this.position == "east" ?
57142                              {top: 0, left: 2, right:2, bottom: 0} :
57143                              {top: 2, left: 0, right:0, bottom: 2});
57144         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
57145         this.bottomTabs = c.tabPosition != "top";
57146         this.autoScroll = c.autoScroll || false;
57147         if(this.autoScroll){
57148             this.bodyEl.setStyle("overflow", "auto");
57149         }else{
57150             this.bodyEl.setStyle("overflow", "hidden");
57151         }
57152         //if(c.titlebar !== false){
57153             if((!c.titlebar && !c.title) || c.titlebar === false){
57154                 this.titleEl.hide();
57155             }else{
57156                 this.titleEl.show();
57157                 if(c.title){
57158                     this.titleTextEl.innerHTML = c.title;
57159                 }
57160             }
57161         //}
57162         this.duration = c.duration || .30;
57163         this.slideDuration = c.slideDuration || .45;
57164         this.config = c;
57165         if(c.collapsed){
57166             this.collapse(true);
57167         }
57168         if(c.hidden){
57169             this.hide();
57170         }
57171     },
57172     /**
57173      * Returns true if this region is currently visible.
57174      * @return {Boolean}
57175      */
57176     isVisible : function(){
57177         return this.visible;
57178     },
57179
57180     /**
57181      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
57182      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
57183      */
57184     setCollapsedTitle : function(title){
57185         title = title || "&#160;";
57186         if(this.collapsedTitleTextEl){
57187             this.collapsedTitleTextEl.innerHTML = title;
57188         }
57189     },
57190
57191     getBox : function(){
57192         var b;
57193         if(!this.collapsed){
57194             b = this.el.getBox(false, true);
57195         }else{
57196             b = this.collapsedEl.getBox(false, true);
57197         }
57198         return b;
57199     },
57200
57201     getMargins : function(){
57202         return this.collapsed ? this.cmargins : this.margins;
57203     },
57204
57205     highlight : function(){
57206         this.el.addClass("x-layout-panel-dragover");
57207     },
57208
57209     unhighlight : function(){
57210         this.el.removeClass("x-layout-panel-dragover");
57211     },
57212
57213     updateBox : function(box){
57214         this.box = box;
57215         if(!this.collapsed){
57216             this.el.dom.style.left = box.x + "px";
57217             this.el.dom.style.top = box.y + "px";
57218             this.updateBody(box.width, box.height);
57219         }else{
57220             this.collapsedEl.dom.style.left = box.x + "px";
57221             this.collapsedEl.dom.style.top = box.y + "px";
57222             this.collapsedEl.setSize(box.width, box.height);
57223         }
57224         if(this.tabs){
57225             this.tabs.autoSizeTabs();
57226         }
57227     },
57228
57229     updateBody : function(w, h){
57230         if(w !== null){
57231             this.el.setWidth(w);
57232             w -= this.el.getBorderWidth("rl");
57233             if(this.config.adjustments){
57234                 w += this.config.adjustments[0];
57235             }
57236         }
57237         if(h !== null){
57238             this.el.setHeight(h);
57239             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
57240             h -= this.el.getBorderWidth("tb");
57241             if(this.config.adjustments){
57242                 h += this.config.adjustments[1];
57243             }
57244             this.bodyEl.setHeight(h);
57245             if(this.tabs){
57246                 h = this.tabs.syncHeight(h);
57247             }
57248         }
57249         if(this.panelSize){
57250             w = w !== null ? w : this.panelSize.width;
57251             h = h !== null ? h : this.panelSize.height;
57252         }
57253         if(this.activePanel){
57254             var el = this.activePanel.getEl();
57255             w = w !== null ? w : el.getWidth();
57256             h = h !== null ? h : el.getHeight();
57257             this.panelSize = {width: w, height: h};
57258             this.activePanel.setSize(w, h);
57259         }
57260         if(Roo.isIE && this.tabs){
57261             this.tabs.el.repaint();
57262         }
57263     },
57264
57265     /**
57266      * Returns the container element for this region.
57267      * @return {Roo.Element}
57268      */
57269     getEl : function(){
57270         return this.el;
57271     },
57272
57273     /**
57274      * Hides this region.
57275      */
57276     hide : function(){
57277         if(!this.collapsed){
57278             this.el.dom.style.left = "-2000px";
57279             this.el.hide();
57280         }else{
57281             this.collapsedEl.dom.style.left = "-2000px";
57282             this.collapsedEl.hide();
57283         }
57284         this.visible = false;
57285         this.fireEvent("visibilitychange", this, false);
57286     },
57287
57288     /**
57289      * Shows this region if it was previously hidden.
57290      */
57291     show : function(){
57292         if(!this.collapsed){
57293             this.el.show();
57294         }else{
57295             this.collapsedEl.show();
57296         }
57297         this.visible = true;
57298         this.fireEvent("visibilitychange", this, true);
57299     },
57300
57301     closeClicked : function(){
57302         if(this.activePanel){
57303             this.remove(this.activePanel);
57304         }
57305     },
57306
57307     collapseClick : function(e){
57308         if(this.isSlid){
57309            e.stopPropagation();
57310            this.slideIn();
57311         }else{
57312            e.stopPropagation();
57313            this.slideOut();
57314         }
57315     },
57316
57317     /**
57318      * Collapses this region.
57319      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
57320      */
57321     collapse : function(skipAnim, skipCheck){
57322         if(this.collapsed) {
57323             return;
57324         }
57325         
57326         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
57327             
57328             this.collapsed = true;
57329             if(this.split){
57330                 this.split.el.hide();
57331             }
57332             if(this.config.animate && skipAnim !== true){
57333                 this.fireEvent("invalidated", this);
57334                 this.animateCollapse();
57335             }else{
57336                 this.el.setLocation(-20000,-20000);
57337                 this.el.hide();
57338                 this.collapsedEl.show();
57339                 this.fireEvent("collapsed", this);
57340                 this.fireEvent("invalidated", this);
57341             }
57342         }
57343         
57344     },
57345
57346     animateCollapse : function(){
57347         // overridden
57348     },
57349
57350     /**
57351      * Expands this region if it was previously collapsed.
57352      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
57353      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
57354      */
57355     expand : function(e, skipAnim){
57356         if(e) {
57357             e.stopPropagation();
57358         }
57359         if(!this.collapsed || this.el.hasActiveFx()) {
57360             return;
57361         }
57362         if(this.isSlid){
57363             this.afterSlideIn();
57364             skipAnim = true;
57365         }
57366         this.collapsed = false;
57367         if(this.config.animate && skipAnim !== true){
57368             this.animateExpand();
57369         }else{
57370             this.el.show();
57371             if(this.split){
57372                 this.split.el.show();
57373             }
57374             this.collapsedEl.setLocation(-2000,-2000);
57375             this.collapsedEl.hide();
57376             this.fireEvent("invalidated", this);
57377             this.fireEvent("expanded", this);
57378         }
57379     },
57380
57381     animateExpand : function(){
57382         // overridden
57383     },
57384
57385     initTabs : function()
57386     {
57387         this.bodyEl.setStyle("overflow", "hidden");
57388         var ts = new Roo.TabPanel(
57389                 this.bodyEl.dom,
57390                 {
57391                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
57392                     disableTooltips: this.config.disableTabTips,
57393                     toolbar : this.config.toolbar
57394                 }
57395         );
57396         if(this.config.hideTabs){
57397             ts.stripWrap.setDisplayed(false);
57398         }
57399         this.tabs = ts;
57400         ts.resizeTabs = this.config.resizeTabs === true;
57401         ts.minTabWidth = this.config.minTabWidth || 40;
57402         ts.maxTabWidth = this.config.maxTabWidth || 250;
57403         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
57404         ts.monitorResize = false;
57405         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
57406         ts.bodyEl.addClass('x-layout-tabs-body');
57407         this.panels.each(this.initPanelAsTab, this);
57408     },
57409
57410     initPanelAsTab : function(panel){
57411         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
57412                     this.config.closeOnTab && panel.isClosable());
57413         if(panel.tabTip !== undefined){
57414             ti.setTooltip(panel.tabTip);
57415         }
57416         ti.on("activate", function(){
57417               this.setActivePanel(panel);
57418         }, this);
57419         if(this.config.closeOnTab){
57420             ti.on("beforeclose", function(t, e){
57421                 e.cancel = true;
57422                 this.remove(panel);
57423             }, this);
57424         }
57425         return ti;
57426     },
57427
57428     updatePanelTitle : function(panel, title){
57429         if(this.activePanel == panel){
57430             this.updateTitle(title);
57431         }
57432         if(this.tabs){
57433             var ti = this.tabs.getTab(panel.getEl().id);
57434             ti.setText(title);
57435             if(panel.tabTip !== undefined){
57436                 ti.setTooltip(panel.tabTip);
57437             }
57438         }
57439     },
57440
57441     updateTitle : function(title){
57442         if(this.titleTextEl && !this.config.title){
57443             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
57444         }
57445     },
57446
57447     setActivePanel : function(panel){
57448         panel = this.getPanel(panel);
57449         if(this.activePanel && this.activePanel != panel){
57450             this.activePanel.setActiveState(false);
57451         }
57452         this.activePanel = panel;
57453         panel.setActiveState(true);
57454         if(this.panelSize){
57455             panel.setSize(this.panelSize.width, this.panelSize.height);
57456         }
57457         if(this.closeBtn){
57458             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
57459         }
57460         this.updateTitle(panel.getTitle());
57461         if(this.tabs){
57462             this.fireEvent("invalidated", this);
57463         }
57464         this.fireEvent("panelactivated", this, panel);
57465     },
57466
57467     /**
57468      * Shows the specified panel.
57469      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
57470      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
57471      */
57472     showPanel : function(panel)
57473     {
57474         panel = this.getPanel(panel);
57475         if(panel){
57476             if(this.tabs){
57477                 var tab = this.tabs.getTab(panel.getEl().id);
57478                 if(tab.isHidden()){
57479                     this.tabs.unhideTab(tab.id);
57480                 }
57481                 tab.activate();
57482             }else{
57483                 this.setActivePanel(panel);
57484             }
57485         }
57486         return panel;
57487     },
57488
57489     /**
57490      * Get the active panel for this region.
57491      * @return {Roo.ContentPanel} The active panel or null
57492      */
57493     getActivePanel : function(){
57494         return this.activePanel;
57495     },
57496
57497     validateVisibility : function(){
57498         if(this.panels.getCount() < 1){
57499             this.updateTitle("&#160;");
57500             this.closeBtn.hide();
57501             this.hide();
57502         }else{
57503             if(!this.isVisible()){
57504                 this.show();
57505             }
57506         }
57507     },
57508
57509     /**
57510      * Adds the passed ContentPanel(s) to this region.
57511      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
57512      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
57513      */
57514     add : function(panel){
57515         if(arguments.length > 1){
57516             for(var i = 0, len = arguments.length; i < len; i++) {
57517                 this.add(arguments[i]);
57518             }
57519             return null;
57520         }
57521         if(this.hasPanel(panel)){
57522             this.showPanel(panel);
57523             return panel;
57524         }
57525         panel.setRegion(this);
57526         this.panels.add(panel);
57527         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
57528             this.bodyEl.dom.appendChild(panel.getEl().dom);
57529             if(panel.background !== true){
57530                 this.setActivePanel(panel);
57531             }
57532             this.fireEvent("paneladded", this, panel);
57533             return panel;
57534         }
57535         if(!this.tabs){
57536             this.initTabs();
57537         }else{
57538             this.initPanelAsTab(panel);
57539         }
57540         if(panel.background !== true){
57541             this.tabs.activate(panel.getEl().id);
57542         }
57543         this.fireEvent("paneladded", this, panel);
57544         return panel;
57545     },
57546
57547     /**
57548      * Hides the tab for the specified panel.
57549      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
57550      */
57551     hidePanel : function(panel){
57552         if(this.tabs && (panel = this.getPanel(panel))){
57553             this.tabs.hideTab(panel.getEl().id);
57554         }
57555     },
57556
57557     /**
57558      * Unhides the tab for a previously hidden panel.
57559      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
57560      */
57561     unhidePanel : function(panel){
57562         if(this.tabs && (panel = this.getPanel(panel))){
57563             this.tabs.unhideTab(panel.getEl().id);
57564         }
57565     },
57566
57567     clearPanels : function(){
57568         while(this.panels.getCount() > 0){
57569              this.remove(this.panels.first());
57570         }
57571     },
57572
57573     /**
57574      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
57575      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
57576      * @param {Boolean} preservePanel Overrides the config preservePanel option
57577      * @return {Roo.ContentPanel} The panel that was removed
57578      */
57579     remove : function(panel, preservePanel){
57580         panel = this.getPanel(panel);
57581         if(!panel){
57582             return null;
57583         }
57584         var e = {};
57585         this.fireEvent("beforeremove", this, panel, e);
57586         if(e.cancel === true){
57587             return null;
57588         }
57589         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
57590         var panelId = panel.getId();
57591         this.panels.removeKey(panelId);
57592         if(preservePanel){
57593             document.body.appendChild(panel.getEl().dom);
57594         }
57595         if(this.tabs){
57596             this.tabs.removeTab(panel.getEl().id);
57597         }else if (!preservePanel){
57598             this.bodyEl.dom.removeChild(panel.getEl().dom);
57599         }
57600         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
57601             var p = this.panels.first();
57602             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
57603             tempEl.appendChild(p.getEl().dom);
57604             this.bodyEl.update("");
57605             this.bodyEl.dom.appendChild(p.getEl().dom);
57606             tempEl = null;
57607             this.updateTitle(p.getTitle());
57608             this.tabs = null;
57609             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
57610             this.setActivePanel(p);
57611         }
57612         panel.setRegion(null);
57613         if(this.activePanel == panel){
57614             this.activePanel = null;
57615         }
57616         if(this.config.autoDestroy !== false && preservePanel !== true){
57617             try{panel.destroy();}catch(e){}
57618         }
57619         this.fireEvent("panelremoved", this, panel);
57620         return panel;
57621     },
57622
57623     /**
57624      * Returns the TabPanel component used by this region
57625      * @return {Roo.TabPanel}
57626      */
57627     getTabs : function(){
57628         return this.tabs;
57629     },
57630
57631     createTool : function(parentEl, className){
57632         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
57633             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
57634         btn.addClassOnOver("x-layout-tools-button-over");
57635         return btn;
57636     }
57637 });/*
57638  * Based on:
57639  * Ext JS Library 1.1.1
57640  * Copyright(c) 2006-2007, Ext JS, LLC.
57641  *
57642  * Originally Released Under LGPL - original licence link has changed is not relivant.
57643  *
57644  * Fork - LGPL
57645  * <script type="text/javascript">
57646  */
57647  
57648
57649
57650 /**
57651  * @class Roo.SplitLayoutRegion
57652  * @extends Roo.LayoutRegion
57653  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
57654  */
57655 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
57656     this.cursor = cursor;
57657     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
57658 };
57659
57660 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
57661     splitTip : "Drag to resize.",
57662     collapsibleSplitTip : "Drag to resize. Double click to hide.",
57663     useSplitTips : false,
57664
57665     applyConfig : function(config){
57666         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
57667         if(config.split){
57668             if(!this.split){
57669                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
57670                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
57671                 /** The SplitBar for this region 
57672                 * @type Roo.SplitBar */
57673                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
57674                 this.split.on("moved", this.onSplitMove, this);
57675                 this.split.useShim = config.useShim === true;
57676                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
57677                 if(this.useSplitTips){
57678                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
57679                 }
57680                 if(config.collapsible){
57681                     this.split.el.on("dblclick", this.collapse,  this);
57682                 }
57683             }
57684             if(typeof config.minSize != "undefined"){
57685                 this.split.minSize = config.minSize;
57686             }
57687             if(typeof config.maxSize != "undefined"){
57688                 this.split.maxSize = config.maxSize;
57689             }
57690             if(config.hideWhenEmpty || config.hidden || config.collapsed){
57691                 this.hideSplitter();
57692             }
57693         }
57694     },
57695
57696     getHMaxSize : function(){
57697          var cmax = this.config.maxSize || 10000;
57698          var center = this.mgr.getRegion("center");
57699          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
57700     },
57701
57702     getVMaxSize : function(){
57703          var cmax = this.config.maxSize || 10000;
57704          var center = this.mgr.getRegion("center");
57705          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
57706     },
57707
57708     onSplitMove : function(split, newSize){
57709         this.fireEvent("resized", this, newSize);
57710     },
57711     
57712     /** 
57713      * Returns the {@link Roo.SplitBar} for this region.
57714      * @return {Roo.SplitBar}
57715      */
57716     getSplitBar : function(){
57717         return this.split;
57718     },
57719     
57720     hide : function(){
57721         this.hideSplitter();
57722         Roo.SplitLayoutRegion.superclass.hide.call(this);
57723     },
57724
57725     hideSplitter : function(){
57726         if(this.split){
57727             this.split.el.setLocation(-2000,-2000);
57728             this.split.el.hide();
57729         }
57730     },
57731
57732     show : function(){
57733         if(this.split){
57734             this.split.el.show();
57735         }
57736         Roo.SplitLayoutRegion.superclass.show.call(this);
57737     },
57738     
57739     beforeSlide: function(){
57740         if(Roo.isGecko){// firefox overflow auto bug workaround
57741             this.bodyEl.clip();
57742             if(this.tabs) {
57743                 this.tabs.bodyEl.clip();
57744             }
57745             if(this.activePanel){
57746                 this.activePanel.getEl().clip();
57747                 
57748                 if(this.activePanel.beforeSlide){
57749                     this.activePanel.beforeSlide();
57750                 }
57751             }
57752         }
57753     },
57754     
57755     afterSlide : function(){
57756         if(Roo.isGecko){// firefox overflow auto bug workaround
57757             this.bodyEl.unclip();
57758             if(this.tabs) {
57759                 this.tabs.bodyEl.unclip();
57760             }
57761             if(this.activePanel){
57762                 this.activePanel.getEl().unclip();
57763                 if(this.activePanel.afterSlide){
57764                     this.activePanel.afterSlide();
57765                 }
57766             }
57767         }
57768     },
57769
57770     initAutoHide : function(){
57771         if(this.autoHide !== false){
57772             if(!this.autoHideHd){
57773                 var st = new Roo.util.DelayedTask(this.slideIn, this);
57774                 this.autoHideHd = {
57775                     "mouseout": function(e){
57776                         if(!e.within(this.el, true)){
57777                             st.delay(500);
57778                         }
57779                     },
57780                     "mouseover" : function(e){
57781                         st.cancel();
57782                     },
57783                     scope : this
57784                 };
57785             }
57786             this.el.on(this.autoHideHd);
57787         }
57788     },
57789
57790     clearAutoHide : function(){
57791         if(this.autoHide !== false){
57792             this.el.un("mouseout", this.autoHideHd.mouseout);
57793             this.el.un("mouseover", this.autoHideHd.mouseover);
57794         }
57795     },
57796
57797     clearMonitor : function(){
57798         Roo.get(document).un("click", this.slideInIf, this);
57799     },
57800
57801     // these names are backwards but not changed for compat
57802     slideOut : function(){
57803         if(this.isSlid || this.el.hasActiveFx()){
57804             return;
57805         }
57806         this.isSlid = true;
57807         if(this.collapseBtn){
57808             this.collapseBtn.hide();
57809         }
57810         this.closeBtnState = this.closeBtn.getStyle('display');
57811         this.closeBtn.hide();
57812         if(this.stickBtn){
57813             this.stickBtn.show();
57814         }
57815         this.el.show();
57816         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
57817         this.beforeSlide();
57818         this.el.setStyle("z-index", 10001);
57819         this.el.slideIn(this.getSlideAnchor(), {
57820             callback: function(){
57821                 this.afterSlide();
57822                 this.initAutoHide();
57823                 Roo.get(document).on("click", this.slideInIf, this);
57824                 this.fireEvent("slideshow", this);
57825             },
57826             scope: this,
57827             block: true
57828         });
57829     },
57830
57831     afterSlideIn : function(){
57832         this.clearAutoHide();
57833         this.isSlid = false;
57834         this.clearMonitor();
57835         this.el.setStyle("z-index", "");
57836         if(this.collapseBtn){
57837             this.collapseBtn.show();
57838         }
57839         this.closeBtn.setStyle('display', this.closeBtnState);
57840         if(this.stickBtn){
57841             this.stickBtn.hide();
57842         }
57843         this.fireEvent("slidehide", this);
57844     },
57845
57846     slideIn : function(cb){
57847         if(!this.isSlid || this.el.hasActiveFx()){
57848             Roo.callback(cb);
57849             return;
57850         }
57851         this.isSlid = false;
57852         this.beforeSlide();
57853         this.el.slideOut(this.getSlideAnchor(), {
57854             callback: function(){
57855                 this.el.setLeftTop(-10000, -10000);
57856                 this.afterSlide();
57857                 this.afterSlideIn();
57858                 Roo.callback(cb);
57859             },
57860             scope: this,
57861             block: true
57862         });
57863     },
57864     
57865     slideInIf : function(e){
57866         if(!e.within(this.el)){
57867             this.slideIn();
57868         }
57869     },
57870
57871     animateCollapse : function(){
57872         this.beforeSlide();
57873         this.el.setStyle("z-index", 20000);
57874         var anchor = this.getSlideAnchor();
57875         this.el.slideOut(anchor, {
57876             callback : function(){
57877                 this.el.setStyle("z-index", "");
57878                 this.collapsedEl.slideIn(anchor, {duration:.3});
57879                 this.afterSlide();
57880                 this.el.setLocation(-10000,-10000);
57881                 this.el.hide();
57882                 this.fireEvent("collapsed", this);
57883             },
57884             scope: this,
57885             block: true
57886         });
57887     },
57888
57889     animateExpand : function(){
57890         this.beforeSlide();
57891         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
57892         this.el.setStyle("z-index", 20000);
57893         this.collapsedEl.hide({
57894             duration:.1
57895         });
57896         this.el.slideIn(this.getSlideAnchor(), {
57897             callback : function(){
57898                 this.el.setStyle("z-index", "");
57899                 this.afterSlide();
57900                 if(this.split){
57901                     this.split.el.show();
57902                 }
57903                 this.fireEvent("invalidated", this);
57904                 this.fireEvent("expanded", this);
57905             },
57906             scope: this,
57907             block: true
57908         });
57909     },
57910
57911     anchors : {
57912         "west" : "left",
57913         "east" : "right",
57914         "north" : "top",
57915         "south" : "bottom"
57916     },
57917
57918     sanchors : {
57919         "west" : "l",
57920         "east" : "r",
57921         "north" : "t",
57922         "south" : "b"
57923     },
57924
57925     canchors : {
57926         "west" : "tl-tr",
57927         "east" : "tr-tl",
57928         "north" : "tl-bl",
57929         "south" : "bl-tl"
57930     },
57931
57932     getAnchor : function(){
57933         return this.anchors[this.position];
57934     },
57935
57936     getCollapseAnchor : function(){
57937         return this.canchors[this.position];
57938     },
57939
57940     getSlideAnchor : function(){
57941         return this.sanchors[this.position];
57942     },
57943
57944     getAlignAdj : function(){
57945         var cm = this.cmargins;
57946         switch(this.position){
57947             case "west":
57948                 return [0, 0];
57949             break;
57950             case "east":
57951                 return [0, 0];
57952             break;
57953             case "north":
57954                 return [0, 0];
57955             break;
57956             case "south":
57957                 return [0, 0];
57958             break;
57959         }
57960     },
57961
57962     getExpandAdj : function(){
57963         var c = this.collapsedEl, cm = this.cmargins;
57964         switch(this.position){
57965             case "west":
57966                 return [-(cm.right+c.getWidth()+cm.left), 0];
57967             break;
57968             case "east":
57969                 return [cm.right+c.getWidth()+cm.left, 0];
57970             break;
57971             case "north":
57972                 return [0, -(cm.top+cm.bottom+c.getHeight())];
57973             break;
57974             case "south":
57975                 return [0, cm.top+cm.bottom+c.getHeight()];
57976             break;
57977         }
57978     }
57979 });/*
57980  * Based on:
57981  * Ext JS Library 1.1.1
57982  * Copyright(c) 2006-2007, Ext JS, LLC.
57983  *
57984  * Originally Released Under LGPL - original licence link has changed is not relivant.
57985  *
57986  * Fork - LGPL
57987  * <script type="text/javascript">
57988  */
57989 /*
57990  * These classes are private internal classes
57991  */
57992 Roo.CenterLayoutRegion = function(mgr, config){
57993     Roo.LayoutRegion.call(this, mgr, config, "center");
57994     this.visible = true;
57995     this.minWidth = config.minWidth || 20;
57996     this.minHeight = config.minHeight || 20;
57997 };
57998
57999 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
58000     hide : function(){
58001         // center panel can't be hidden
58002     },
58003     
58004     show : function(){
58005         // center panel can't be hidden
58006     },
58007     
58008     getMinWidth: function(){
58009         return this.minWidth;
58010     },
58011     
58012     getMinHeight: function(){
58013         return this.minHeight;
58014     }
58015 });
58016
58017
58018 Roo.NorthLayoutRegion = function(mgr, config){
58019     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
58020     if(this.split){
58021         this.split.placement = Roo.SplitBar.TOP;
58022         this.split.orientation = Roo.SplitBar.VERTICAL;
58023         this.split.el.addClass("x-layout-split-v");
58024     }
58025     var size = config.initialSize || config.height;
58026     if(typeof size != "undefined"){
58027         this.el.setHeight(size);
58028     }
58029 };
58030 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
58031     orientation: Roo.SplitBar.VERTICAL,
58032     getBox : function(){
58033         if(this.collapsed){
58034             return this.collapsedEl.getBox();
58035         }
58036         var box = this.el.getBox();
58037         if(this.split){
58038             box.height += this.split.el.getHeight();
58039         }
58040         return box;
58041     },
58042     
58043     updateBox : function(box){
58044         if(this.split && !this.collapsed){
58045             box.height -= this.split.el.getHeight();
58046             this.split.el.setLeft(box.x);
58047             this.split.el.setTop(box.y+box.height);
58048             this.split.el.setWidth(box.width);
58049         }
58050         if(this.collapsed){
58051             this.updateBody(box.width, null);
58052         }
58053         Roo.LayoutRegion.prototype.updateBox.call(this, box);
58054     }
58055 });
58056
58057 Roo.SouthLayoutRegion = function(mgr, config){
58058     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
58059     if(this.split){
58060         this.split.placement = Roo.SplitBar.BOTTOM;
58061         this.split.orientation = Roo.SplitBar.VERTICAL;
58062         this.split.el.addClass("x-layout-split-v");
58063     }
58064     var size = config.initialSize || config.height;
58065     if(typeof size != "undefined"){
58066         this.el.setHeight(size);
58067     }
58068 };
58069 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
58070     orientation: Roo.SplitBar.VERTICAL,
58071     getBox : function(){
58072         if(this.collapsed){
58073             return this.collapsedEl.getBox();
58074         }
58075         var box = this.el.getBox();
58076         if(this.split){
58077             var sh = this.split.el.getHeight();
58078             box.height += sh;
58079             box.y -= sh;
58080         }
58081         return box;
58082     },
58083     
58084     updateBox : function(box){
58085         if(this.split && !this.collapsed){
58086             var sh = this.split.el.getHeight();
58087             box.height -= sh;
58088             box.y += sh;
58089             this.split.el.setLeft(box.x);
58090             this.split.el.setTop(box.y-sh);
58091             this.split.el.setWidth(box.width);
58092         }
58093         if(this.collapsed){
58094             this.updateBody(box.width, null);
58095         }
58096         Roo.LayoutRegion.prototype.updateBox.call(this, box);
58097     }
58098 });
58099
58100 Roo.EastLayoutRegion = function(mgr, config){
58101     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
58102     if(this.split){
58103         this.split.placement = Roo.SplitBar.RIGHT;
58104         this.split.orientation = Roo.SplitBar.HORIZONTAL;
58105         this.split.el.addClass("x-layout-split-h");
58106     }
58107     var size = config.initialSize || config.width;
58108     if(typeof size != "undefined"){
58109         this.el.setWidth(size);
58110     }
58111 };
58112 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
58113     orientation: Roo.SplitBar.HORIZONTAL,
58114     getBox : function(){
58115         if(this.collapsed){
58116             return this.collapsedEl.getBox();
58117         }
58118         var box = this.el.getBox();
58119         if(this.split){
58120             var sw = this.split.el.getWidth();
58121             box.width += sw;
58122             box.x -= sw;
58123         }
58124         return box;
58125     },
58126
58127     updateBox : function(box){
58128         if(this.split && !this.collapsed){
58129             var sw = this.split.el.getWidth();
58130             box.width -= sw;
58131             this.split.el.setLeft(box.x);
58132             this.split.el.setTop(box.y);
58133             this.split.el.setHeight(box.height);
58134             box.x += sw;
58135         }
58136         if(this.collapsed){
58137             this.updateBody(null, box.height);
58138         }
58139         Roo.LayoutRegion.prototype.updateBox.call(this, box);
58140     }
58141 });
58142
58143 Roo.WestLayoutRegion = function(mgr, config){
58144     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
58145     if(this.split){
58146         this.split.placement = Roo.SplitBar.LEFT;
58147         this.split.orientation = Roo.SplitBar.HORIZONTAL;
58148         this.split.el.addClass("x-layout-split-h");
58149     }
58150     var size = config.initialSize || config.width;
58151     if(typeof size != "undefined"){
58152         this.el.setWidth(size);
58153     }
58154 };
58155 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
58156     orientation: Roo.SplitBar.HORIZONTAL,
58157     getBox : function(){
58158         if(this.collapsed){
58159             return this.collapsedEl.getBox();
58160         }
58161         var box = this.el.getBox();
58162         if(this.split){
58163             box.width += this.split.el.getWidth();
58164         }
58165         return box;
58166     },
58167     
58168     updateBox : function(box){
58169         if(this.split && !this.collapsed){
58170             var sw = this.split.el.getWidth();
58171             box.width -= sw;
58172             this.split.el.setLeft(box.x+box.width);
58173             this.split.el.setTop(box.y);
58174             this.split.el.setHeight(box.height);
58175         }
58176         if(this.collapsed){
58177             this.updateBody(null, box.height);
58178         }
58179         Roo.LayoutRegion.prototype.updateBox.call(this, box);
58180     }
58181 });
58182 /*
58183  * Based on:
58184  * Ext JS Library 1.1.1
58185  * Copyright(c) 2006-2007, Ext JS, LLC.
58186  *
58187  * Originally Released Under LGPL - original licence link has changed is not relivant.
58188  *
58189  * Fork - LGPL
58190  * <script type="text/javascript">
58191  */
58192  
58193  
58194 /*
58195  * Private internal class for reading and applying state
58196  */
58197 Roo.LayoutStateManager = function(layout){
58198      // default empty state
58199      this.state = {
58200         north: {},
58201         south: {},
58202         east: {},
58203         west: {}       
58204     };
58205 };
58206
58207 Roo.LayoutStateManager.prototype = {
58208     init : function(layout, provider){
58209         this.provider = provider;
58210         var state = provider.get(layout.id+"-layout-state");
58211         if(state){
58212             var wasUpdating = layout.isUpdating();
58213             if(!wasUpdating){
58214                 layout.beginUpdate();
58215             }
58216             for(var key in state){
58217                 if(typeof state[key] != "function"){
58218                     var rstate = state[key];
58219                     var r = layout.getRegion(key);
58220                     if(r && rstate){
58221                         if(rstate.size){
58222                             r.resizeTo(rstate.size);
58223                         }
58224                         if(rstate.collapsed == true){
58225                             r.collapse(true);
58226                         }else{
58227                             r.expand(null, true);
58228                         }
58229                     }
58230                 }
58231             }
58232             if(!wasUpdating){
58233                 layout.endUpdate();
58234             }
58235             this.state = state; 
58236         }
58237         this.layout = layout;
58238         layout.on("regionresized", this.onRegionResized, this);
58239         layout.on("regioncollapsed", this.onRegionCollapsed, this);
58240         layout.on("regionexpanded", this.onRegionExpanded, this);
58241     },
58242     
58243     storeState : function(){
58244         this.provider.set(this.layout.id+"-layout-state", this.state);
58245     },
58246     
58247     onRegionResized : function(region, newSize){
58248         this.state[region.getPosition()].size = newSize;
58249         this.storeState();
58250     },
58251     
58252     onRegionCollapsed : function(region){
58253         this.state[region.getPosition()].collapsed = true;
58254         this.storeState();
58255     },
58256     
58257     onRegionExpanded : function(region){
58258         this.state[region.getPosition()].collapsed = false;
58259         this.storeState();
58260     }
58261 };/*
58262  * Based on:
58263  * Ext JS Library 1.1.1
58264  * Copyright(c) 2006-2007, Ext JS, LLC.
58265  *
58266  * Originally Released Under LGPL - original licence link has changed is not relivant.
58267  *
58268  * Fork - LGPL
58269  * <script type="text/javascript">
58270  */
58271 /**
58272  * @class Roo.ContentPanel
58273  * @extends Roo.util.Observable
58274  * @children Roo.form.Form Roo.JsonView Roo.View
58275  * @parent Roo.BorderLayout Roo.LayoutDialog builder
58276  * A basic ContentPanel element.
58277  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
58278  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
58279  * @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
58280  * @cfg {Boolean}   closable      True if the panel can be closed/removed
58281  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
58282  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
58283  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
58284  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
58285  * @cfg {String} title          The title for this panel
58286  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
58287  * @cfg {String} url            Calls {@link #setUrl} with this value
58288  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
58289  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
58290  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
58291  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
58292  * @cfg {String}    style  Extra style to add to the content panel
58293  * @cfg {Roo.menu.Menu} menu  popup menu
58294
58295  * @constructor
58296  * Create a new ContentPanel.
58297  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
58298  * @param {String/Object} config A string to set only the title or a config object
58299  * @param {String} content (optional) Set the HTML content for this panel
58300  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
58301  */
58302 Roo.ContentPanel = function(el, config, content){
58303     
58304      
58305     /*
58306     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
58307         config = el;
58308         el = Roo.id();
58309     }
58310     if (config && config.parentLayout) { 
58311         el = config.parentLayout.el.createChild(); 
58312     }
58313     */
58314     if(el.autoCreate){ // xtype is available if this is called from factory
58315         config = el;
58316         el = Roo.id();
58317     }
58318     this.el = Roo.get(el);
58319     if(!this.el && config && config.autoCreate){
58320         if(typeof config.autoCreate == "object"){
58321             if(!config.autoCreate.id){
58322                 config.autoCreate.id = config.id||el;
58323             }
58324             this.el = Roo.DomHelper.append(document.body,
58325                         config.autoCreate, true);
58326         }else{
58327             this.el = Roo.DomHelper.append(document.body,
58328                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
58329         }
58330     }
58331     
58332     
58333     this.closable = false;
58334     this.loaded = false;
58335     this.active = false;
58336     if(typeof config == "string"){
58337         this.title = config;
58338     }else{
58339         Roo.apply(this, config);
58340     }
58341     
58342     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
58343         this.wrapEl = this.el.wrap();
58344         this.toolbar.container = this.el.insertSibling(false, 'before');
58345         this.toolbar = new Roo.Toolbar(this.toolbar);
58346     }
58347     
58348     // xtype created footer. - not sure if will work as we normally have to render first..
58349     if (this.footer && !this.footer.el && this.footer.xtype) {
58350         if (!this.wrapEl) {
58351             this.wrapEl = this.el.wrap();
58352         }
58353     
58354         this.footer.container = this.wrapEl.createChild();
58355          
58356         this.footer = Roo.factory(this.footer, Roo);
58357         
58358     }
58359     
58360     if(this.resizeEl){
58361         this.resizeEl = Roo.get(this.resizeEl, true);
58362     }else{
58363         this.resizeEl = this.el;
58364     }
58365     // handle view.xtype
58366     
58367  
58368     
58369     
58370     this.addEvents({
58371         /**
58372          * @event activate
58373          * Fires when this panel is activated. 
58374          * @param {Roo.ContentPanel} this
58375          */
58376         "activate" : true,
58377         /**
58378          * @event deactivate
58379          * Fires when this panel is activated. 
58380          * @param {Roo.ContentPanel} this
58381          */
58382         "deactivate" : true,
58383
58384         /**
58385          * @event resize
58386          * Fires when this panel is resized if fitToFrame is true.
58387          * @param {Roo.ContentPanel} this
58388          * @param {Number} width The width after any component adjustments
58389          * @param {Number} height The height after any component adjustments
58390          */
58391         "resize" : true,
58392         
58393          /**
58394          * @event render
58395          * Fires when this tab is created
58396          * @param {Roo.ContentPanel} this
58397          */
58398         "render" : true
58399          
58400         
58401     });
58402     
58403
58404     
58405     
58406     if(this.autoScroll){
58407         this.resizeEl.setStyle("overflow", "auto");
58408     } else {
58409         // fix randome scrolling
58410         this.el.on('scroll', function() {
58411             Roo.log('fix random scolling');
58412             this.scrollTo('top',0); 
58413         });
58414     }
58415     content = content || this.content;
58416     if(content){
58417         this.setContent(content);
58418     }
58419     if(config && config.url){
58420         this.setUrl(this.url, this.params, this.loadOnce);
58421     }
58422     
58423     
58424     
58425     Roo.ContentPanel.superclass.constructor.call(this);
58426     
58427     if (this.view && typeof(this.view.xtype) != 'undefined') {
58428         this.view.el = this.el.appendChild(document.createElement("div"));
58429         this.view = Roo.factory(this.view); 
58430         this.view.render  &&  this.view.render(false, '');  
58431     }
58432     
58433     
58434     this.fireEvent('render', this);
58435 };
58436
58437 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
58438     tabTip:'',
58439     setRegion : function(region){
58440         this.region = region;
58441         if(region){
58442            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
58443         }else{
58444            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
58445         } 
58446     },
58447     
58448     /**
58449      * Returns the toolbar for this Panel if one was configured. 
58450      * @return {Roo.Toolbar} 
58451      */
58452     getToolbar : function(){
58453         return this.toolbar;
58454     },
58455     
58456     setActiveState : function(active){
58457         this.active = active;
58458         if(!active){
58459             this.fireEvent("deactivate", this);
58460         }else{
58461             this.fireEvent("activate", this);
58462         }
58463     },
58464     /**
58465      * Updates this panel's element
58466      * @param {String} content The new content
58467      * @param {Boolean} loadScripts (optional) true to look for and process scripts
58468     */
58469     setContent : function(content, loadScripts){
58470         this.el.update(content, loadScripts);
58471     },
58472
58473     ignoreResize : function(w, h){
58474         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
58475             return true;
58476         }else{
58477             this.lastSize = {width: w, height: h};
58478             return false;
58479         }
58480     },
58481     /**
58482      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
58483      * @return {Roo.UpdateManager} The UpdateManager
58484      */
58485     getUpdateManager : function(){
58486         return this.el.getUpdateManager();
58487     },
58488      /**
58489      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
58490      * @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:
58491 <pre><code>
58492 panel.load({
58493     url: "your-url.php",
58494     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
58495     callback: yourFunction,
58496     scope: yourObject, //(optional scope)
58497     discardUrl: false,
58498     nocache: false,
58499     text: "Loading...",
58500     timeout: 30,
58501     scripts: false
58502 });
58503 </code></pre>
58504      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
58505      * 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.
58506      * @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}
58507      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
58508      * @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.
58509      * @return {Roo.ContentPanel} this
58510      */
58511     load : function(){
58512         var um = this.el.getUpdateManager();
58513         um.update.apply(um, arguments);
58514         return this;
58515     },
58516
58517
58518     /**
58519      * 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.
58520      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
58521      * @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)
58522      * @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)
58523      * @return {Roo.UpdateManager} The UpdateManager
58524      */
58525     setUrl : function(url, params, loadOnce){
58526         if(this.refreshDelegate){
58527             this.removeListener("activate", this.refreshDelegate);
58528         }
58529         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
58530         this.on("activate", this.refreshDelegate);
58531         return this.el.getUpdateManager();
58532     },
58533     
58534     _handleRefresh : function(url, params, loadOnce){
58535         if(!loadOnce || !this.loaded){
58536             var updater = this.el.getUpdateManager();
58537             updater.update(url, params, this._setLoaded.createDelegate(this));
58538         }
58539     },
58540     
58541     _setLoaded : function(){
58542         this.loaded = true;
58543     }, 
58544     
58545     /**
58546      * Returns this panel's id
58547      * @return {String} 
58548      */
58549     getId : function(){
58550         return this.el.id;
58551     },
58552     
58553     /** 
58554      * Returns this panel's element - used by regiosn to add.
58555      * @return {Roo.Element} 
58556      */
58557     getEl : function(){
58558         return this.wrapEl || this.el;
58559     },
58560     
58561     adjustForComponents : function(width, height)
58562     {
58563         //Roo.log('adjustForComponents ');
58564         if(this.resizeEl != this.el){
58565             width -= this.el.getFrameWidth('lr');
58566             height -= this.el.getFrameWidth('tb');
58567         }
58568         if(this.toolbar){
58569             var te = this.toolbar.getEl();
58570             height -= te.getHeight();
58571             te.setWidth(width);
58572         }
58573         if(this.footer){
58574             var te = this.footer.getEl();
58575             //Roo.log("footer:" + te.getHeight());
58576             
58577             height -= te.getHeight();
58578             te.setWidth(width);
58579         }
58580         
58581         
58582         if(this.adjustments){
58583             width += this.adjustments[0];
58584             height += this.adjustments[1];
58585         }
58586         return {"width": width, "height": height};
58587     },
58588     
58589     setSize : function(width, height){
58590         if(this.fitToFrame && !this.ignoreResize(width, height)){
58591             if(this.fitContainer && this.resizeEl != this.el){
58592                 this.el.setSize(width, height);
58593             }
58594             var size = this.adjustForComponents(width, height);
58595             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
58596             this.fireEvent('resize', this, size.width, size.height);
58597         }
58598     },
58599     
58600     /**
58601      * Returns this panel's title
58602      * @return {String} 
58603      */
58604     getTitle : function(){
58605         return this.title;
58606     },
58607     
58608     /**
58609      * Set this panel's title
58610      * @param {String} title
58611      */
58612     setTitle : function(title){
58613         this.title = title;
58614         if(this.region){
58615             this.region.updatePanelTitle(this, title);
58616         }
58617     },
58618     
58619     /**
58620      * Returns true is this panel was configured to be closable
58621      * @return {Boolean} 
58622      */
58623     isClosable : function(){
58624         return this.closable;
58625     },
58626     
58627     beforeSlide : function(){
58628         this.el.clip();
58629         this.resizeEl.clip();
58630     },
58631     
58632     afterSlide : function(){
58633         this.el.unclip();
58634         this.resizeEl.unclip();
58635     },
58636     
58637     /**
58638      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
58639      *   Will fail silently if the {@link #setUrl} method has not been called.
58640      *   This does not activate the panel, just updates its content.
58641      */
58642     refresh : function(){
58643         if(this.refreshDelegate){
58644            this.loaded = false;
58645            this.refreshDelegate();
58646         }
58647     },
58648     
58649     /**
58650      * Destroys this panel
58651      */
58652     destroy : function(){
58653         this.el.removeAllListeners();
58654         var tempEl = document.createElement("span");
58655         tempEl.appendChild(this.el.dom);
58656         tempEl.innerHTML = "";
58657         this.el.remove();
58658         this.el = null;
58659     },
58660     
58661     /**
58662      * form - if the content panel contains a form - this is a reference to it.
58663      * @type {Roo.form.Form}
58664      */
58665     form : false,
58666     /**
58667      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
58668      *    This contains a reference to it.
58669      * @type {Roo.View}
58670      */
58671     view : false,
58672     
58673       /**
58674      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
58675      * <pre><code>
58676
58677 layout.addxtype({
58678        xtype : 'Form',
58679        items: [ .... ]
58680    }
58681 );
58682
58683 </code></pre>
58684      * @param {Object} cfg Xtype definition of item to add.
58685      */
58686     
58687     addxtype : function(cfg) {
58688         // add form..
58689         if (cfg.xtype.match(/^Form$/)) {
58690             
58691             var el;
58692             //if (this.footer) {
58693             //    el = this.footer.container.insertSibling(false, 'before');
58694             //} else {
58695                 el = this.el.createChild();
58696             //}
58697
58698             this.form = new  Roo.form.Form(cfg);
58699             
58700             
58701             if ( this.form.allItems.length) {
58702                 this.form.render(el.dom);
58703             }
58704             return this.form;
58705         }
58706         // should only have one of theses..
58707         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
58708             // views.. should not be just added - used named prop 'view''
58709             
58710             cfg.el = this.el.appendChild(document.createElement("div"));
58711             // factory?
58712             
58713             var ret = new Roo.factory(cfg);
58714              
58715              ret.render && ret.render(false, ''); // render blank..
58716             this.view = ret;
58717             return ret;
58718         }
58719         return false;
58720     }
58721 });
58722
58723 /**
58724  * @class Roo.GridPanel
58725  * @extends Roo.ContentPanel
58726  * @parent Roo.BorderLayout Roo.LayoutDialog builder
58727  * @constructor
58728  * Create a new GridPanel.
58729  * @cfg {Roo.grid.Grid} grid The grid for this panel
58730  */
58731 Roo.GridPanel = function(grid, config){
58732     
58733     // universal ctor...
58734     if (typeof(grid.grid) != 'undefined') {
58735         config = grid;
58736         grid = config.grid;
58737     }
58738     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
58739         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
58740         
58741     this.wrapper.dom.appendChild(grid.getGridEl().dom);
58742     
58743     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
58744     
58745     if(this.toolbar){
58746         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
58747     }
58748     // xtype created footer. - not sure if will work as we normally have to render first..
58749     if (this.footer && !this.footer.el && this.footer.xtype) {
58750         
58751         this.footer.container = this.grid.getView().getFooterPanel(true);
58752         this.footer.dataSource = this.grid.dataSource;
58753         this.footer = Roo.factory(this.footer, Roo);
58754         
58755     }
58756     
58757     grid.monitorWindowResize = false; // turn off autosizing
58758     grid.autoHeight = false;
58759     grid.autoWidth = false;
58760     this.grid = grid;
58761     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
58762 };
58763
58764 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
58765     getId : function(){
58766         return this.grid.id;
58767     },
58768     
58769     /**
58770      * Returns the grid for this panel
58771      * @return {Roo.grid.Grid} 
58772      */
58773     getGrid : function(){
58774         return this.grid;    
58775     },
58776     
58777     setSize : function(width, height){
58778         if(!this.ignoreResize(width, height)){
58779             var grid = this.grid;
58780             var size = this.adjustForComponents(width, height);
58781             grid.getGridEl().setSize(size.width, size.height);
58782             grid.autoSize();
58783         }
58784     },
58785     
58786     beforeSlide : function(){
58787         this.grid.getView().scroller.clip();
58788     },
58789     
58790     afterSlide : function(){
58791         this.grid.getView().scroller.unclip();
58792     },
58793     
58794     destroy : function(){
58795         this.grid.destroy();
58796         delete this.grid;
58797         Roo.GridPanel.superclass.destroy.call(this); 
58798     }
58799 });
58800
58801
58802 /**
58803  * @class Roo.NestedLayoutPanel
58804  * @extends Roo.ContentPanel
58805  * @parent Roo.BorderLayout Roo.LayoutDialog builder
58806  * @cfg Roo.BorderLayout} layout   [required] The layout for this panel
58807  *
58808  * 
58809  * @constructor
58810  * Create a new NestedLayoutPanel.
58811  * 
58812  * 
58813  * @param {Roo.BorderLayout} layout [required] The layout for this panel
58814  * @param {String/Object} config A string to set only the title or a config object
58815  */
58816 Roo.NestedLayoutPanel = function(layout, config)
58817 {
58818     // construct with only one argument..
58819     /* FIXME - implement nicer consturctors
58820     if (layout.layout) {
58821         config = layout;
58822         layout = config.layout;
58823         delete config.layout;
58824     }
58825     if (layout.xtype && !layout.getEl) {
58826         // then layout needs constructing..
58827         layout = Roo.factory(layout, Roo);
58828     }
58829     */
58830     
58831     
58832     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
58833     
58834     layout.monitorWindowResize = false; // turn off autosizing
58835     this.layout = layout;
58836     this.layout.getEl().addClass("x-layout-nested-layout");
58837     
58838     
58839     
58840     
58841 };
58842
58843 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
58844
58845     setSize : function(width, height){
58846         if(!this.ignoreResize(width, height)){
58847             var size = this.adjustForComponents(width, height);
58848             var el = this.layout.getEl();
58849             el.setSize(size.width, size.height);
58850             var touch = el.dom.offsetWidth;
58851             this.layout.layout();
58852             // ie requires a double layout on the first pass
58853             if(Roo.isIE && !this.initialized){
58854                 this.initialized = true;
58855                 this.layout.layout();
58856             }
58857         }
58858     },
58859     
58860     // activate all subpanels if not currently active..
58861     
58862     setActiveState : function(active){
58863         this.active = active;
58864         if(!active){
58865             this.fireEvent("deactivate", this);
58866             return;
58867         }
58868         
58869         this.fireEvent("activate", this);
58870         // not sure if this should happen before or after..
58871         if (!this.layout) {
58872             return; // should not happen..
58873         }
58874         var reg = false;
58875         for (var r in this.layout.regions) {
58876             reg = this.layout.getRegion(r);
58877             if (reg.getActivePanel()) {
58878                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
58879                 reg.setActivePanel(reg.getActivePanel());
58880                 continue;
58881             }
58882             if (!reg.panels.length) {
58883                 continue;
58884             }
58885             reg.showPanel(reg.getPanel(0));
58886         }
58887         
58888         
58889         
58890         
58891     },
58892     
58893     /**
58894      * Returns the nested BorderLayout for this panel
58895      * @return {Roo.BorderLayout} 
58896      */
58897     getLayout : function(){
58898         return this.layout;
58899     },
58900     
58901      /**
58902      * Adds a xtype elements to the layout of the nested panel
58903      * <pre><code>
58904
58905 panel.addxtype({
58906        xtype : 'ContentPanel',
58907        region: 'west',
58908        items: [ .... ]
58909    }
58910 );
58911
58912 panel.addxtype({
58913         xtype : 'NestedLayoutPanel',
58914         region: 'west',
58915         layout: {
58916            center: { },
58917            west: { }   
58918         },
58919         items : [ ... list of content panels or nested layout panels.. ]
58920    }
58921 );
58922 </code></pre>
58923      * @param {Object} cfg Xtype definition of item to add.
58924      */
58925     addxtype : function(cfg) {
58926         return this.layout.addxtype(cfg);
58927     
58928     }
58929 });
58930
58931 Roo.ScrollPanel = function(el, config, content){
58932     config = config || {};
58933     config.fitToFrame = true;
58934     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
58935     
58936     this.el.dom.style.overflow = "hidden";
58937     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
58938     this.el.removeClass("x-layout-inactive-content");
58939     this.el.on("mousewheel", this.onWheel, this);
58940
58941     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
58942     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
58943     up.unselectable(); down.unselectable();
58944     up.on("click", this.scrollUp, this);
58945     down.on("click", this.scrollDown, this);
58946     up.addClassOnOver("x-scroller-btn-over");
58947     down.addClassOnOver("x-scroller-btn-over");
58948     up.addClassOnClick("x-scroller-btn-click");
58949     down.addClassOnClick("x-scroller-btn-click");
58950     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
58951
58952     this.resizeEl = this.el;
58953     this.el = wrap; this.up = up; this.down = down;
58954 };
58955
58956 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
58957     increment : 100,
58958     wheelIncrement : 5,
58959     scrollUp : function(){
58960         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
58961     },
58962
58963     scrollDown : function(){
58964         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
58965     },
58966
58967     afterScroll : function(){
58968         var el = this.resizeEl;
58969         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
58970         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
58971         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
58972     },
58973
58974     setSize : function(){
58975         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
58976         this.afterScroll();
58977     },
58978
58979     onWheel : function(e){
58980         var d = e.getWheelDelta();
58981         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
58982         this.afterScroll();
58983         e.stopEvent();
58984     },
58985
58986     setContent : function(content, loadScripts){
58987         this.resizeEl.update(content, loadScripts);
58988     }
58989
58990 });
58991
58992
58993
58994 /**
58995  * @class Roo.TreePanel
58996  * @extends Roo.ContentPanel
58997  * @parent Roo.BorderLayout Roo.LayoutDialog builder
58998  * Treepanel component
58999  * 
59000  * @constructor
59001  * Create a new TreePanel. - defaults to fit/scoll contents.
59002  * @param {String/Object} config A string to set only the panel's title, or a config object
59003  */
59004 Roo.TreePanel = function(config){
59005     var el = config.el;
59006     var tree = config.tree;
59007     delete config.tree; 
59008     delete config.el; // hopefull!
59009     
59010     // wrapper for IE7 strict & safari scroll issue
59011     
59012     var treeEl = el.createChild();
59013     config.resizeEl = treeEl;
59014     
59015     
59016     
59017     Roo.TreePanel.superclass.constructor.call(this, el, config);
59018  
59019  
59020     this.tree = new Roo.tree.TreePanel(treeEl , tree);
59021     //console.log(tree);
59022     this.on('activate', function()
59023     {
59024         if (this.tree.rendered) {
59025             return;
59026         }
59027         //console.log('render tree');
59028         this.tree.render();
59029     });
59030     // this should not be needed.. - it's actually the 'el' that resizes?
59031     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
59032     
59033     //this.on('resize',  function (cp, w, h) {
59034     //        this.tree.innerCt.setWidth(w);
59035     //        this.tree.innerCt.setHeight(h);
59036     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
59037     //});
59038
59039         
59040     
59041 };
59042
59043 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
59044     fitToFrame : true,
59045     autoScroll : true,
59046     /*
59047      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
59048      */
59049     tree : false
59050
59051 });
59052
59053
59054
59055
59056
59057
59058
59059
59060
59061
59062
59063 /*
59064  * Based on:
59065  * Ext JS Library 1.1.1
59066  * Copyright(c) 2006-2007, Ext JS, LLC.
59067  *
59068  * Originally Released Under LGPL - original licence link has changed is not relivant.
59069  *
59070  * Fork - LGPL
59071  * <script type="text/javascript">
59072  */
59073  
59074
59075 /**
59076  * @class Roo.ReaderLayout
59077  * @extends Roo.BorderLayout
59078  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
59079  * center region containing two nested regions (a top one for a list view and one for item preview below),
59080  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
59081  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
59082  * expedites the setup of the overall layout and regions for this common application style.
59083  * Example:
59084  <pre><code>
59085 var reader = new Roo.ReaderLayout();
59086 var CP = Roo.ContentPanel;  // shortcut for adding
59087
59088 reader.beginUpdate();
59089 reader.add("north", new CP("north", "North"));
59090 reader.add("west", new CP("west", {title: "West"}));
59091 reader.add("east", new CP("east", {title: "East"}));
59092
59093 reader.regions.listView.add(new CP("listView", "List"));
59094 reader.regions.preview.add(new CP("preview", "Preview"));
59095 reader.endUpdate();
59096 </code></pre>
59097 * @constructor
59098 * Create a new ReaderLayout
59099 * @param {Object} config Configuration options
59100 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
59101 * document.body if omitted)
59102 */
59103 Roo.ReaderLayout = function(config, renderTo){
59104     var c = config || {size:{}};
59105     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
59106         north: c.north !== false ? Roo.apply({
59107             split:false,
59108             initialSize: 32,
59109             titlebar: false
59110         }, c.north) : false,
59111         west: c.west !== false ? Roo.apply({
59112             split:true,
59113             initialSize: 200,
59114             minSize: 175,
59115             maxSize: 400,
59116             titlebar: true,
59117             collapsible: true,
59118             animate: true,
59119             margins:{left:5,right:0,bottom:5,top:5},
59120             cmargins:{left:5,right:5,bottom:5,top:5}
59121         }, c.west) : false,
59122         east: c.east !== false ? Roo.apply({
59123             split:true,
59124             initialSize: 200,
59125             minSize: 175,
59126             maxSize: 400,
59127             titlebar: true,
59128             collapsible: true,
59129             animate: true,
59130             margins:{left:0,right:5,bottom:5,top:5},
59131             cmargins:{left:5,right:5,bottom:5,top:5}
59132         }, c.east) : false,
59133         center: Roo.apply({
59134             tabPosition: 'top',
59135             autoScroll:false,
59136             closeOnTab: true,
59137             titlebar:false,
59138             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
59139         }, c.center)
59140     });
59141
59142     this.el.addClass('x-reader');
59143
59144     this.beginUpdate();
59145
59146     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
59147         south: c.preview !== false ? Roo.apply({
59148             split:true,
59149             initialSize: 200,
59150             minSize: 100,
59151             autoScroll:true,
59152             collapsible:true,
59153             titlebar: true,
59154             cmargins:{top:5,left:0, right:0, bottom:0}
59155         }, c.preview) : false,
59156         center: Roo.apply({
59157             autoScroll:false,
59158             titlebar:false,
59159             minHeight:200
59160         }, c.listView)
59161     });
59162     this.add('center', new Roo.NestedLayoutPanel(inner,
59163             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
59164
59165     this.endUpdate();
59166
59167     this.regions.preview = inner.getRegion('south');
59168     this.regions.listView = inner.getRegion('center');
59169 };
59170
59171 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
59172  * Based on:
59173  * Ext JS Library 1.1.1
59174  * Copyright(c) 2006-2007, Ext JS, LLC.
59175  *
59176  * Originally Released Under LGPL - original licence link has changed is not relivant.
59177  *
59178  * Fork - LGPL
59179  * <script type="text/javascript">
59180  */
59181  
59182 /**
59183  * @class Roo.grid.Grid
59184  * @extends Roo.util.Observable
59185  * This class represents the primary interface of a component based grid control.
59186  * <br><br>Usage:<pre><code>
59187  var grid = new Roo.grid.Grid("my-container-id", {
59188      ds: myDataStore,
59189      cm: myColModel,
59190      selModel: mySelectionModel,
59191      autoSizeColumns: true,
59192      monitorWindowResize: false,
59193      trackMouseOver: true
59194  });
59195  // set any options
59196  grid.render();
59197  * </code></pre>
59198  * <b>Common Problems:</b><br/>
59199  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
59200  * element will correct this<br/>
59201  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
59202  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
59203  * are unpredictable.<br/>
59204  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
59205  * grid to calculate dimensions/offsets.<br/>
59206   * @constructor
59207  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59208  * The container MUST have some type of size defined for the grid to fill. The container will be
59209  * automatically set to position relative if it isn't already.
59210  * @param {Object} config A config object that sets properties on this grid.
59211  */
59212 Roo.grid.Grid = function(container, config){
59213         // initialize the container
59214         this.container = Roo.get(container);
59215         this.container.update("");
59216         this.container.setStyle("overflow", "hidden");
59217     this.container.addClass('x-grid-container');
59218
59219     this.id = this.container.id;
59220
59221     Roo.apply(this, config);
59222     // check and correct shorthanded configs
59223     if(this.ds){
59224         this.dataSource = this.ds;
59225         delete this.ds;
59226     }
59227     if(this.cm){
59228         this.colModel = this.cm;
59229         delete this.cm;
59230     }
59231     if(this.sm){
59232         this.selModel = this.sm;
59233         delete this.sm;
59234     }
59235
59236     if (this.selModel) {
59237         this.selModel = Roo.factory(this.selModel, Roo.grid);
59238         this.sm = this.selModel;
59239         this.sm.xmodule = this.xmodule || false;
59240     }
59241     if (typeof(this.colModel.config) == 'undefined') {
59242         this.colModel = new Roo.grid.ColumnModel(this.colModel);
59243         this.cm = this.colModel;
59244         this.cm.xmodule = this.xmodule || false;
59245     }
59246     if (this.dataSource) {
59247         this.dataSource= Roo.factory(this.dataSource, Roo.data);
59248         this.ds = this.dataSource;
59249         this.ds.xmodule = this.xmodule || false;
59250          
59251     }
59252     
59253     
59254     
59255     if(this.width){
59256         this.container.setWidth(this.width);
59257     }
59258
59259     if(this.height){
59260         this.container.setHeight(this.height);
59261     }
59262     /** @private */
59263         this.addEvents({
59264         // raw events
59265         /**
59266          * @event click
59267          * The raw click event for the entire grid.
59268          * @param {Roo.EventObject} e
59269          */
59270         "click" : true,
59271         /**
59272          * @event dblclick
59273          * The raw dblclick event for the entire grid.
59274          * @param {Roo.EventObject} e
59275          */
59276         "dblclick" : true,
59277         /**
59278          * @event contextmenu
59279          * The raw contextmenu event for the entire grid.
59280          * @param {Roo.EventObject} e
59281          */
59282         "contextmenu" : true,
59283         /**
59284          * @event mousedown
59285          * The raw mousedown event for the entire grid.
59286          * @param {Roo.EventObject} e
59287          */
59288         "mousedown" : true,
59289         /**
59290          * @event mouseup
59291          * The raw mouseup event for the entire grid.
59292          * @param {Roo.EventObject} e
59293          */
59294         "mouseup" : true,
59295         /**
59296          * @event mouseover
59297          * The raw mouseover event for the entire grid.
59298          * @param {Roo.EventObject} e
59299          */
59300         "mouseover" : true,
59301         /**
59302          * @event mouseout
59303          * The raw mouseout event for the entire grid.
59304          * @param {Roo.EventObject} e
59305          */
59306         "mouseout" : true,
59307         /**
59308          * @event keypress
59309          * The raw keypress event for the entire grid.
59310          * @param {Roo.EventObject} e
59311          */
59312         "keypress" : true,
59313         /**
59314          * @event keydown
59315          * The raw keydown event for the entire grid.
59316          * @param {Roo.EventObject} e
59317          */
59318         "keydown" : true,
59319
59320         // custom events
59321
59322         /**
59323          * @event cellclick
59324          * Fires when a cell is clicked
59325          * @param {Grid} this
59326          * @param {Number} rowIndex
59327          * @param {Number} columnIndex
59328          * @param {Roo.EventObject} e
59329          */
59330         "cellclick" : true,
59331         /**
59332          * @event celldblclick
59333          * Fires when a cell is double clicked
59334          * @param {Grid} this
59335          * @param {Number} rowIndex
59336          * @param {Number} columnIndex
59337          * @param {Roo.EventObject} e
59338          */
59339         "celldblclick" : true,
59340         /**
59341          * @event rowclick
59342          * Fires when a row is clicked
59343          * @param {Grid} this
59344          * @param {Number} rowIndex
59345          * @param {Roo.EventObject} e
59346          */
59347         "rowclick" : true,
59348         /**
59349          * @event rowdblclick
59350          * Fires when a row is double clicked
59351          * @param {Grid} this
59352          * @param {Number} rowIndex
59353          * @param {Roo.EventObject} e
59354          */
59355         "rowdblclick" : true,
59356         /**
59357          * @event headerclick
59358          * Fires when a header is clicked
59359          * @param {Grid} this
59360          * @param {Number} columnIndex
59361          * @param {Roo.EventObject} e
59362          */
59363         "headerclick" : true,
59364         /**
59365          * @event headerdblclick
59366          * Fires when a header cell is double clicked
59367          * @param {Grid} this
59368          * @param {Number} columnIndex
59369          * @param {Roo.EventObject} e
59370          */
59371         "headerdblclick" : true,
59372         /**
59373          * @event rowcontextmenu
59374          * Fires when a row is right clicked
59375          * @param {Grid} this
59376          * @param {Number} rowIndex
59377          * @param {Roo.EventObject} e
59378          */
59379         "rowcontextmenu" : true,
59380         /**
59381          * @event cellcontextmenu
59382          * Fires when a cell is right clicked
59383          * @param {Grid} this
59384          * @param {Number} rowIndex
59385          * @param {Number} cellIndex
59386          * @param {Roo.EventObject} e
59387          */
59388          "cellcontextmenu" : true,
59389         /**
59390          * @event headercontextmenu
59391          * Fires when a header is right clicked
59392          * @param {Grid} this
59393          * @param {Number} columnIndex
59394          * @param {Roo.EventObject} e
59395          */
59396         "headercontextmenu" : true,
59397         /**
59398          * @event bodyscroll
59399          * Fires when the body element is scrolled
59400          * @param {Number} scrollLeft
59401          * @param {Number} scrollTop
59402          */
59403         "bodyscroll" : true,
59404         /**
59405          * @event columnresize
59406          * Fires when the user resizes a column
59407          * @param {Number} columnIndex
59408          * @param {Number} newSize
59409          */
59410         "columnresize" : true,
59411         /**
59412          * @event columnmove
59413          * Fires when the user moves a column
59414          * @param {Number} oldIndex
59415          * @param {Number} newIndex
59416          */
59417         "columnmove" : true,
59418         /**
59419          * @event startdrag
59420          * Fires when row(s) start being dragged
59421          * @param {Grid} this
59422          * @param {Roo.GridDD} dd The drag drop object
59423          * @param {event} e The raw browser event
59424          */
59425         "startdrag" : true,
59426         /**
59427          * @event enddrag
59428          * Fires when a drag operation is complete
59429          * @param {Grid} this
59430          * @param {Roo.GridDD} dd The drag drop object
59431          * @param {event} e The raw browser event
59432          */
59433         "enddrag" : true,
59434         /**
59435          * @event dragdrop
59436          * Fires when dragged row(s) are dropped on a valid DD target
59437          * @param {Grid} this
59438          * @param {Roo.GridDD} dd The drag drop object
59439          * @param {String} targetId The target drag drop object
59440          * @param {event} e The raw browser event
59441          */
59442         "dragdrop" : true,
59443         /**
59444          * @event dragover
59445          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59446          * @param {Grid} this
59447          * @param {Roo.GridDD} dd The drag drop object
59448          * @param {String} targetId The target drag drop object
59449          * @param {event} e The raw browser event
59450          */
59451         "dragover" : true,
59452         /**
59453          * @event dragenter
59454          *  Fires when the dragged row(s) first cross another DD target while being dragged
59455          * @param {Grid} this
59456          * @param {Roo.GridDD} dd The drag drop object
59457          * @param {String} targetId The target drag drop object
59458          * @param {event} e The raw browser event
59459          */
59460         "dragenter" : true,
59461         /**
59462          * @event dragout
59463          * Fires when the dragged row(s) leave another DD target while being dragged
59464          * @param {Grid} this
59465          * @param {Roo.GridDD} dd The drag drop object
59466          * @param {String} targetId The target drag drop object
59467          * @param {event} e The raw browser event
59468          */
59469         "dragout" : true,
59470         /**
59471          * @event rowclass
59472          * Fires when a row is rendered, so you can change add a style to it.
59473          * @param {GridView} gridview   The grid view
59474          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59475          */
59476         'rowclass' : true,
59477
59478         /**
59479          * @event render
59480          * Fires when the grid is rendered
59481          * @param {Grid} grid
59482          */
59483         'render' : true
59484     });
59485
59486     Roo.grid.Grid.superclass.constructor.call(this);
59487 };
59488 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
59489     
59490     /**
59491          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
59492          */
59493         /**
59494          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
59495          */
59496         /**
59497          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
59498          */
59499         /**
59500          * @cfg {Roo.grid.Store} ds The data store for the grid
59501          */
59502         /**
59503          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
59504          */
59505         /**
59506      * @cfg {String} ddGroup - drag drop group.
59507      */
59508       /**
59509      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
59510      */
59511
59512     /**
59513      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
59514      */
59515     minColumnWidth : 25,
59516
59517     /**
59518      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
59519      * <b>on initial render.</b> It is more efficient to explicitly size the columns
59520      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
59521      */
59522     autoSizeColumns : false,
59523
59524     /**
59525      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
59526      */
59527     autoSizeHeaders : true,
59528
59529     /**
59530      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
59531      */
59532     monitorWindowResize : true,
59533
59534     /**
59535      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
59536      * rows measured to get a columns size. Default is 0 (all rows).
59537      */
59538     maxRowsToMeasure : 0,
59539
59540     /**
59541      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
59542      */
59543     trackMouseOver : true,
59544
59545     /**
59546     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
59547     */
59548       /**
59549     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
59550     */
59551     
59552     /**
59553     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
59554     */
59555     enableDragDrop : false,
59556     
59557     /**
59558     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
59559     */
59560     enableColumnMove : true,
59561     
59562     /**
59563     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
59564     */
59565     enableColumnHide : true,
59566     
59567     /**
59568     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
59569     */
59570     enableRowHeightSync : false,
59571     
59572     /**
59573     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
59574     */
59575     stripeRows : true,
59576     
59577     /**
59578     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
59579     */
59580     autoHeight : false,
59581
59582     /**
59583      * @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.
59584      */
59585     autoExpandColumn : false,
59586
59587     /**
59588     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
59589     * Default is 50.
59590     */
59591     autoExpandMin : 50,
59592
59593     /**
59594     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
59595     */
59596     autoExpandMax : 1000,
59597
59598     /**
59599     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
59600     */
59601     view : null,
59602
59603     /**
59604     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
59605     */
59606     loadMask : false,
59607     /**
59608     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
59609     */
59610     dropTarget: false,
59611      /**
59612     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
59613     */ 
59614     sortColMenu : false,
59615     
59616     // private
59617     rendered : false,
59618
59619     /**
59620     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
59621     * of a fixed width. Default is false.
59622     */
59623     /**
59624     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
59625     */
59626     
59627     
59628     /**
59629     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
59630     * %0 is replaced with the number of selected rows.
59631     */
59632     ddText : "{0} selected row{1}",
59633     
59634     
59635     /**
59636      * Called once after all setup has been completed and the grid is ready to be rendered.
59637      * @return {Roo.grid.Grid} this
59638      */
59639     render : function()
59640     {
59641         var c = this.container;
59642         // try to detect autoHeight/width mode
59643         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
59644             this.autoHeight = true;
59645         }
59646         var view = this.getView();
59647         view.init(this);
59648
59649         c.on("click", this.onClick, this);
59650         c.on("dblclick", this.onDblClick, this);
59651         c.on("contextmenu", this.onContextMenu, this);
59652         c.on("keydown", this.onKeyDown, this);
59653         if (Roo.isTouch) {
59654             c.on("touchstart", this.onTouchStart, this);
59655         }
59656
59657         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
59658
59659         this.getSelectionModel().init(this);
59660
59661         view.render();
59662
59663         if(this.loadMask){
59664             this.loadMask = new Roo.LoadMask(this.container,
59665                     Roo.apply({store:this.dataSource}, this.loadMask));
59666         }
59667         
59668         
59669         if (this.toolbar && this.toolbar.xtype) {
59670             this.toolbar.container = this.getView().getHeaderPanel(true);
59671             this.toolbar = new Roo.Toolbar(this.toolbar);
59672         }
59673         if (this.footer && this.footer.xtype) {
59674             this.footer.dataSource = this.getDataSource();
59675             this.footer.container = this.getView().getFooterPanel(true);
59676             this.footer = Roo.factory(this.footer, Roo);
59677         }
59678         if (this.dropTarget && this.dropTarget.xtype) {
59679             delete this.dropTarget.xtype;
59680             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
59681         }
59682         
59683         
59684         this.rendered = true;
59685         this.fireEvent('render', this);
59686         return this;
59687     },
59688
59689     /**
59690      * Reconfigures the grid to use a different Store and Column Model.
59691      * The View will be bound to the new objects and refreshed.
59692      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
59693      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
59694      */
59695     reconfigure : function(dataSource, colModel){
59696         if(this.loadMask){
59697             this.loadMask.destroy();
59698             this.loadMask = new Roo.LoadMask(this.container,
59699                     Roo.apply({store:dataSource}, this.loadMask));
59700         }
59701         this.view.bind(dataSource, colModel);
59702         this.dataSource = dataSource;
59703         this.colModel = colModel;
59704         this.view.refresh(true);
59705     },
59706     /**
59707      * addColumns
59708      * Add's a column, default at the end..
59709      
59710      * @param {int} position to add (default end)
59711      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
59712      */
59713     addColumns : function(pos, ar)
59714     {
59715         
59716         for (var i =0;i< ar.length;i++) {
59717             var cfg = ar[i];
59718             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
59719             this.cm.lookup[cfg.id] = cfg;
59720         }
59721         
59722         
59723         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
59724             pos = this.cm.config.length; //this.cm.config.push(cfg);
59725         } 
59726         pos = Math.max(0,pos);
59727         ar.unshift(0);
59728         ar.unshift(pos);
59729         this.cm.config.splice.apply(this.cm.config, ar);
59730         
59731         
59732         
59733         this.view.generateRules(this.cm);
59734         this.view.refresh(true);
59735         
59736     },
59737     
59738     
59739     
59740     
59741     // private
59742     onKeyDown : function(e){
59743         this.fireEvent("keydown", e);
59744     },
59745
59746     /**
59747      * Destroy this grid.
59748      * @param {Boolean} removeEl True to remove the element
59749      */
59750     destroy : function(removeEl, keepListeners){
59751         if(this.loadMask){
59752             this.loadMask.destroy();
59753         }
59754         var c = this.container;
59755         c.removeAllListeners();
59756         this.view.destroy();
59757         this.colModel.purgeListeners();
59758         if(!keepListeners){
59759             this.purgeListeners();
59760         }
59761         c.update("");
59762         if(removeEl === true){
59763             c.remove();
59764         }
59765     },
59766
59767     // private
59768     processEvent : function(name, e){
59769         // does this fire select???
59770         //Roo.log('grid:processEvent '  + name);
59771         
59772         if (name != 'touchstart' ) {
59773             this.fireEvent(name, e);    
59774         }
59775         
59776         var t = e.getTarget();
59777         var v = this.view;
59778         var header = v.findHeaderIndex(t);
59779         if(header !== false){
59780             var ename = name == 'touchstart' ? 'click' : name;
59781              
59782             this.fireEvent("header" + ename, this, header, e);
59783         }else{
59784             var row = v.findRowIndex(t);
59785             var cell = v.findCellIndex(t);
59786             if (name == 'touchstart') {
59787                 // first touch is always a click.
59788                 // hopefull this happens after selection is updated.?
59789                 name = false;
59790                 
59791                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
59792                     var cs = this.selModel.getSelectedCell();
59793                     if (row == cs[0] && cell == cs[1]){
59794                         name = 'dblclick';
59795                     }
59796                 }
59797                 if (typeof(this.selModel.getSelections) != 'undefined') {
59798                     var cs = this.selModel.getSelections();
59799                     var ds = this.dataSource;
59800                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
59801                         name = 'dblclick';
59802                     }
59803                 }
59804                 if (!name) {
59805                     return;
59806                 }
59807             }
59808             
59809             
59810             if(row !== false){
59811                 this.fireEvent("row" + name, this, row, e);
59812                 if(cell !== false){
59813                     this.fireEvent("cell" + name, this, row, cell, e);
59814                 }
59815             }
59816         }
59817     },
59818
59819     // private
59820     onClick : function(e){
59821         this.processEvent("click", e);
59822     },
59823    // private
59824     onTouchStart : function(e){
59825         this.processEvent("touchstart", e);
59826     },
59827
59828     // private
59829     onContextMenu : function(e, t){
59830         this.processEvent("contextmenu", e);
59831     },
59832
59833     // private
59834     onDblClick : function(e){
59835         this.processEvent("dblclick", e);
59836     },
59837
59838     // private
59839     walkCells : function(row, col, step, fn, scope){
59840         var cm = this.colModel, clen = cm.getColumnCount();
59841         var ds = this.dataSource, rlen = ds.getCount(), first = true;
59842         if(step < 0){
59843             if(col < 0){
59844                 row--;
59845                 first = false;
59846             }
59847             while(row >= 0){
59848                 if(!first){
59849                     col = clen-1;
59850                 }
59851                 first = false;
59852                 while(col >= 0){
59853                     if(fn.call(scope || this, row, col, cm) === true){
59854                         return [row, col];
59855                     }
59856                     col--;
59857                 }
59858                 row--;
59859             }
59860         } else {
59861             if(col >= clen){
59862                 row++;
59863                 first = false;
59864             }
59865             while(row < rlen){
59866                 if(!first){
59867                     col = 0;
59868                 }
59869                 first = false;
59870                 while(col < clen){
59871                     if(fn.call(scope || this, row, col, cm) === true){
59872                         return [row, col];
59873                     }
59874                     col++;
59875                 }
59876                 row++;
59877             }
59878         }
59879         return null;
59880     },
59881
59882     // private
59883     getSelections : function(){
59884         return this.selModel.getSelections();
59885     },
59886
59887     /**
59888      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
59889      * but if manual update is required this method will initiate it.
59890      */
59891     autoSize : function(){
59892         if(this.rendered){
59893             this.view.layout();
59894             if(this.view.adjustForScroll){
59895                 this.view.adjustForScroll();
59896             }
59897         }
59898     },
59899
59900     /**
59901      * Returns the grid's underlying element.
59902      * @return {Element} The element
59903      */
59904     getGridEl : function(){
59905         return this.container;
59906     },
59907
59908     // private for compatibility, overridden by editor grid
59909     stopEditing : function(){},
59910
59911     /**
59912      * Returns the grid's SelectionModel.
59913      * @return {SelectionModel}
59914      */
59915     getSelectionModel : function(){
59916         if(!this.selModel){
59917             this.selModel = new Roo.grid.RowSelectionModel();
59918         }
59919         return this.selModel;
59920     },
59921
59922     /**
59923      * Returns the grid's DataSource.
59924      * @return {DataSource}
59925      */
59926     getDataSource : function(){
59927         return this.dataSource;
59928     },
59929
59930     /**
59931      * Returns the grid's ColumnModel.
59932      * @return {ColumnModel}
59933      */
59934     getColumnModel : function(){
59935         return this.colModel;
59936     },
59937
59938     /**
59939      * Returns the grid's GridView object.
59940      * @return {GridView}
59941      */
59942     getView : function(){
59943         if(!this.view){
59944             this.view = new Roo.grid.GridView(this.viewConfig);
59945             this.relayEvents(this.view, [
59946                 "beforerowremoved", "beforerowsinserted",
59947                 "beforerefresh", "rowremoved",
59948                 "rowsinserted", "rowupdated" ,"refresh"
59949             ]);
59950         }
59951         return this.view;
59952     },
59953     /**
59954      * Called to get grid's drag proxy text, by default returns this.ddText.
59955      * Override this to put something different in the dragged text.
59956      * @return {String}
59957      */
59958     getDragDropText : function(){
59959         var count = this.selModel.getCount();
59960         return String.format(this.ddText, count, count == 1 ? '' : 's');
59961     }
59962 });
59963 /*
59964  * Based on:
59965  * Ext JS Library 1.1.1
59966  * Copyright(c) 2006-2007, Ext JS, LLC.
59967  *
59968  * Originally Released Under LGPL - original licence link has changed is not relivant.
59969  *
59970  * Fork - LGPL
59971  * <script type="text/javascript">
59972  */
59973  /**
59974  * @class Roo.grid.AbstractGridView
59975  * @extends Roo.util.Observable
59976  * @abstract
59977  * Abstract base class for grid Views
59978  * @constructor
59979  */
59980 Roo.grid.AbstractGridView = function(){
59981         this.grid = null;
59982         
59983         this.events = {
59984             "beforerowremoved" : true,
59985             "beforerowsinserted" : true,
59986             "beforerefresh" : true,
59987             "rowremoved" : true,
59988             "rowsinserted" : true,
59989             "rowupdated" : true,
59990             "refresh" : true
59991         };
59992     Roo.grid.AbstractGridView.superclass.constructor.call(this);
59993 };
59994
59995 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
59996     rowClass : "x-grid-row",
59997     cellClass : "x-grid-cell",
59998     tdClass : "x-grid-td",
59999     hdClass : "x-grid-hd",
60000     splitClass : "x-grid-hd-split",
60001     
60002     init: function(grid){
60003         this.grid = grid;
60004                 var cid = this.grid.getGridEl().id;
60005         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
60006         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
60007         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
60008         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
60009         },
60010         
60011     getColumnRenderers : function(){
60012         var renderers = [];
60013         var cm = this.grid.colModel;
60014         var colCount = cm.getColumnCount();
60015         for(var i = 0; i < colCount; i++){
60016             renderers[i] = cm.getRenderer(i);
60017         }
60018         return renderers;
60019     },
60020     
60021     getColumnIds : function(){
60022         var ids = [];
60023         var cm = this.grid.colModel;
60024         var colCount = cm.getColumnCount();
60025         for(var i = 0; i < colCount; i++){
60026             ids[i] = cm.getColumnId(i);
60027         }
60028         return ids;
60029     },
60030     
60031     getDataIndexes : function(){
60032         if(!this.indexMap){
60033             this.indexMap = this.buildIndexMap();
60034         }
60035         return this.indexMap.colToData;
60036     },
60037     
60038     getColumnIndexByDataIndex : function(dataIndex){
60039         if(!this.indexMap){
60040             this.indexMap = this.buildIndexMap();
60041         }
60042         return this.indexMap.dataToCol[dataIndex];
60043     },
60044     
60045     /**
60046      * Set a css style for a column dynamically. 
60047      * @param {Number} colIndex The index of the column
60048      * @param {String} name The css property name
60049      * @param {String} value The css value
60050      */
60051     setCSSStyle : function(colIndex, name, value){
60052         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
60053         Roo.util.CSS.updateRule(selector, name, value);
60054     },
60055     
60056     generateRules : function(cm){
60057         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
60058         Roo.util.CSS.removeStyleSheet(rulesId);
60059         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
60060             var cid = cm.getColumnId(i);
60061             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
60062                          this.tdSelector, cid, " {\n}\n",
60063                          this.hdSelector, cid, " {\n}\n",
60064                          this.splitSelector, cid, " {\n}\n");
60065         }
60066         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
60067     }
60068 });/*
60069  * Based on:
60070  * Ext JS Library 1.1.1
60071  * Copyright(c) 2006-2007, Ext JS, LLC.
60072  *
60073  * Originally Released Under LGPL - original licence link has changed is not relivant.
60074  *
60075  * Fork - LGPL
60076  * <script type="text/javascript">
60077  */
60078
60079 // private
60080 // This is a support class used internally by the Grid components
60081 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
60082     this.grid = grid;
60083     this.view = grid.getView();
60084     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
60085     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
60086     if(hd2){
60087         this.setHandleElId(Roo.id(hd));
60088         this.setOuterHandleElId(Roo.id(hd2));
60089     }
60090     this.scroll = false;
60091 };
60092 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
60093     maxDragWidth: 120,
60094     getDragData : function(e){
60095         var t = Roo.lib.Event.getTarget(e);
60096         var h = this.view.findHeaderCell(t);
60097         if(h){
60098             return {ddel: h.firstChild, header:h};
60099         }
60100         return false;
60101     },
60102
60103     onInitDrag : function(e){
60104         this.view.headersDisabled = true;
60105         var clone = this.dragData.ddel.cloneNode(true);
60106         clone.id = Roo.id();
60107         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
60108         this.proxy.update(clone);
60109         return true;
60110     },
60111
60112     afterValidDrop : function(){
60113         var v = this.view;
60114         setTimeout(function(){
60115             v.headersDisabled = false;
60116         }, 50);
60117     },
60118
60119     afterInvalidDrop : function(){
60120         var v = this.view;
60121         setTimeout(function(){
60122             v.headersDisabled = false;
60123         }, 50);
60124     }
60125 });
60126 /*
60127  * Based on:
60128  * Ext JS Library 1.1.1
60129  * Copyright(c) 2006-2007, Ext JS, LLC.
60130  *
60131  * Originally Released Under LGPL - original licence link has changed is not relivant.
60132  *
60133  * Fork - LGPL
60134  * <script type="text/javascript">
60135  */
60136 // private
60137 // This is a support class used internally by the Grid components
60138 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
60139     this.grid = grid;
60140     this.view = grid.getView();
60141     // split the proxies so they don't interfere with mouse events
60142     this.proxyTop = Roo.DomHelper.append(document.body, {
60143         cls:"col-move-top", html:"&#160;"
60144     }, true);
60145     this.proxyBottom = Roo.DomHelper.append(document.body, {
60146         cls:"col-move-bottom", html:"&#160;"
60147     }, true);
60148     this.proxyTop.hide = this.proxyBottom.hide = function(){
60149         this.setLeftTop(-100,-100);
60150         this.setStyle("visibility", "hidden");
60151     };
60152     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
60153     // temporarily disabled
60154     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
60155     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
60156 };
60157 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
60158     proxyOffsets : [-4, -9],
60159     fly: Roo.Element.fly,
60160
60161     getTargetFromEvent : function(e){
60162         var t = Roo.lib.Event.getTarget(e);
60163         var cindex = this.view.findCellIndex(t);
60164         if(cindex !== false){
60165             return this.view.getHeaderCell(cindex);
60166         }
60167         return null;
60168     },
60169
60170     nextVisible : function(h){
60171         var v = this.view, cm = this.grid.colModel;
60172         h = h.nextSibling;
60173         while(h){
60174             if(!cm.isHidden(v.getCellIndex(h))){
60175                 return h;
60176             }
60177             h = h.nextSibling;
60178         }
60179         return null;
60180     },
60181
60182     prevVisible : function(h){
60183         var v = this.view, cm = this.grid.colModel;
60184         h = h.prevSibling;
60185         while(h){
60186             if(!cm.isHidden(v.getCellIndex(h))){
60187                 return h;
60188             }
60189             h = h.prevSibling;
60190         }
60191         return null;
60192     },
60193
60194     positionIndicator : function(h, n, e){
60195         var x = Roo.lib.Event.getPageX(e);
60196         var r = Roo.lib.Dom.getRegion(n.firstChild);
60197         var px, pt, py = r.top + this.proxyOffsets[1];
60198         if((r.right - x) <= (r.right-r.left)/2){
60199             px = r.right+this.view.borderWidth;
60200             pt = "after";
60201         }else{
60202             px = r.left;
60203             pt = "before";
60204         }
60205         var oldIndex = this.view.getCellIndex(h);
60206         var newIndex = this.view.getCellIndex(n);
60207
60208         if(this.grid.colModel.isFixed(newIndex)){
60209             return false;
60210         }
60211
60212         var locked = this.grid.colModel.isLocked(newIndex);
60213
60214         if(pt == "after"){
60215             newIndex++;
60216         }
60217         if(oldIndex < newIndex){
60218             newIndex--;
60219         }
60220         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
60221             return false;
60222         }
60223         px +=  this.proxyOffsets[0];
60224         this.proxyTop.setLeftTop(px, py);
60225         this.proxyTop.show();
60226         if(!this.bottomOffset){
60227             this.bottomOffset = this.view.mainHd.getHeight();
60228         }
60229         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
60230         this.proxyBottom.show();
60231         return pt;
60232     },
60233
60234     onNodeEnter : function(n, dd, e, data){
60235         if(data.header != n){
60236             this.positionIndicator(data.header, n, e);
60237         }
60238     },
60239
60240     onNodeOver : function(n, dd, e, data){
60241         var result = false;
60242         if(data.header != n){
60243             result = this.positionIndicator(data.header, n, e);
60244         }
60245         if(!result){
60246             this.proxyTop.hide();
60247             this.proxyBottom.hide();
60248         }
60249         return result ? this.dropAllowed : this.dropNotAllowed;
60250     },
60251
60252     onNodeOut : function(n, dd, e, data){
60253         this.proxyTop.hide();
60254         this.proxyBottom.hide();
60255     },
60256
60257     onNodeDrop : function(n, dd, e, data){
60258         var h = data.header;
60259         if(h != n){
60260             var cm = this.grid.colModel;
60261             var x = Roo.lib.Event.getPageX(e);
60262             var r = Roo.lib.Dom.getRegion(n.firstChild);
60263             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
60264             var oldIndex = this.view.getCellIndex(h);
60265             var newIndex = this.view.getCellIndex(n);
60266             var locked = cm.isLocked(newIndex);
60267             if(pt == "after"){
60268                 newIndex++;
60269             }
60270             if(oldIndex < newIndex){
60271                 newIndex--;
60272             }
60273             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
60274                 return false;
60275             }
60276             cm.setLocked(oldIndex, locked, true);
60277             cm.moveColumn(oldIndex, newIndex);
60278             this.grid.fireEvent("columnmove", oldIndex, newIndex);
60279             return true;
60280         }
60281         return false;
60282     }
60283 });
60284 /*
60285  * Based on:
60286  * Ext JS Library 1.1.1
60287  * Copyright(c) 2006-2007, Ext JS, LLC.
60288  *
60289  * Originally Released Under LGPL - original licence link has changed is not relivant.
60290  *
60291  * Fork - LGPL
60292  * <script type="text/javascript">
60293  */
60294   
60295 /**
60296  * @class Roo.grid.GridView
60297  * @extends Roo.util.Observable
60298  *
60299  * @constructor
60300  * @param {Object} config
60301  */
60302 Roo.grid.GridView = function(config){
60303     Roo.grid.GridView.superclass.constructor.call(this);
60304     this.el = null;
60305
60306     Roo.apply(this, config);
60307 };
60308
60309 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
60310
60311     unselectable :  'unselectable="on"',
60312     unselectableCls :  'x-unselectable',
60313     
60314     
60315     rowClass : "x-grid-row",
60316
60317     cellClass : "x-grid-col",
60318
60319     tdClass : "x-grid-td",
60320
60321     hdClass : "x-grid-hd",
60322
60323     splitClass : "x-grid-split",
60324
60325     sortClasses : ["sort-asc", "sort-desc"],
60326
60327     enableMoveAnim : false,
60328
60329     hlColor: "C3DAF9",
60330
60331     dh : Roo.DomHelper,
60332
60333     fly : Roo.Element.fly,
60334
60335     css : Roo.util.CSS,
60336
60337     borderWidth: 1,
60338
60339     splitOffset: 3,
60340
60341     scrollIncrement : 22,
60342
60343     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
60344
60345     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
60346
60347     bind : function(ds, cm){
60348         if(this.ds){
60349             this.ds.un("load", this.onLoad, this);
60350             this.ds.un("datachanged", this.onDataChange, this);
60351             this.ds.un("add", this.onAdd, this);
60352             this.ds.un("remove", this.onRemove, this);
60353             this.ds.un("update", this.onUpdate, this);
60354             this.ds.un("clear", this.onClear, this);
60355         }
60356         if(ds){
60357             ds.on("load", this.onLoad, this);
60358             ds.on("datachanged", this.onDataChange, this);
60359             ds.on("add", this.onAdd, this);
60360             ds.on("remove", this.onRemove, this);
60361             ds.on("update", this.onUpdate, this);
60362             ds.on("clear", this.onClear, this);
60363         }
60364         this.ds = ds;
60365
60366         if(this.cm){
60367             this.cm.un("widthchange", this.onColWidthChange, this);
60368             this.cm.un("headerchange", this.onHeaderChange, this);
60369             this.cm.un("hiddenchange", this.onHiddenChange, this);
60370             this.cm.un("columnmoved", this.onColumnMove, this);
60371             this.cm.un("columnlockchange", this.onColumnLock, this);
60372         }
60373         if(cm){
60374             this.generateRules(cm);
60375             cm.on("widthchange", this.onColWidthChange, this);
60376             cm.on("headerchange", this.onHeaderChange, this);
60377             cm.on("hiddenchange", this.onHiddenChange, this);
60378             cm.on("columnmoved", this.onColumnMove, this);
60379             cm.on("columnlockchange", this.onColumnLock, this);
60380         }
60381         this.cm = cm;
60382     },
60383
60384     init: function(grid){
60385         Roo.grid.GridView.superclass.init.call(this, grid);
60386
60387         this.bind(grid.dataSource, grid.colModel);
60388
60389         grid.on("headerclick", this.handleHeaderClick, this);
60390
60391         if(grid.trackMouseOver){
60392             grid.on("mouseover", this.onRowOver, this);
60393             grid.on("mouseout", this.onRowOut, this);
60394         }
60395         grid.cancelTextSelection = function(){};
60396         this.gridId = grid.id;
60397
60398         var tpls = this.templates || {};
60399
60400         if(!tpls.master){
60401             tpls.master = new Roo.Template(
60402                '<div class="x-grid" hidefocus="true">',
60403                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
60404                   '<div class="x-grid-topbar"></div>',
60405                   '<div class="x-grid-scroller"><div></div></div>',
60406                   '<div class="x-grid-locked">',
60407                       '<div class="x-grid-header">{lockedHeader}</div>',
60408                       '<div class="x-grid-body">{lockedBody}</div>',
60409                   "</div>",
60410                   '<div class="x-grid-viewport">',
60411                       '<div class="x-grid-header">{header}</div>',
60412                       '<div class="x-grid-body">{body}</div>',
60413                   "</div>",
60414                   '<div class="x-grid-bottombar"></div>',
60415                  
60416                   '<div class="x-grid-resize-proxy">&#160;</div>',
60417                "</div>"
60418             );
60419             tpls.master.disableformats = true;
60420         }
60421
60422         if(!tpls.header){
60423             tpls.header = new Roo.Template(
60424                '<table border="0" cellspacing="0" cellpadding="0">',
60425                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
60426                "</table>{splits}"
60427             );
60428             tpls.header.disableformats = true;
60429         }
60430         tpls.header.compile();
60431
60432         if(!tpls.hcell){
60433             tpls.hcell = new Roo.Template(
60434                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
60435                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
60436                 "</div></td>"
60437              );
60438              tpls.hcell.disableFormats = true;
60439         }
60440         tpls.hcell.compile();
60441
60442         if(!tpls.hsplit){
60443             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
60444                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
60445             tpls.hsplit.disableFormats = true;
60446         }
60447         tpls.hsplit.compile();
60448
60449         if(!tpls.body){
60450             tpls.body = new Roo.Template(
60451                '<table border="0" cellspacing="0" cellpadding="0">',
60452                "<tbody>{rows}</tbody>",
60453                "</table>"
60454             );
60455             tpls.body.disableFormats = true;
60456         }
60457         tpls.body.compile();
60458
60459         if(!tpls.row){
60460             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
60461             tpls.row.disableFormats = true;
60462         }
60463         tpls.row.compile();
60464
60465         if(!tpls.cell){
60466             tpls.cell = new Roo.Template(
60467                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
60468                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
60469                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
60470                 "</td>"
60471             );
60472             tpls.cell.disableFormats = true;
60473         }
60474         tpls.cell.compile();
60475
60476         this.templates = tpls;
60477     },
60478
60479     // remap these for backwards compat
60480     onColWidthChange : function(){
60481         this.updateColumns.apply(this, arguments);
60482     },
60483     onHeaderChange : function(){
60484         this.updateHeaders.apply(this, arguments);
60485     }, 
60486     onHiddenChange : function(){
60487         this.handleHiddenChange.apply(this, arguments);
60488     },
60489     onColumnMove : function(){
60490         this.handleColumnMove.apply(this, arguments);
60491     },
60492     onColumnLock : function(){
60493         this.handleLockChange.apply(this, arguments);
60494     },
60495
60496     onDataChange : function(){
60497         this.refresh();
60498         this.updateHeaderSortState();
60499     },
60500
60501     onClear : function(){
60502         this.refresh();
60503     },
60504
60505     onUpdate : function(ds, record){
60506         this.refreshRow(record);
60507     },
60508
60509     refreshRow : function(record){
60510         var ds = this.ds, index;
60511         if(typeof record == 'number'){
60512             index = record;
60513             record = ds.getAt(index);
60514         }else{
60515             index = ds.indexOf(record);
60516         }
60517         this.insertRows(ds, index, index, true);
60518         this.onRemove(ds, record, index+1, true);
60519         this.syncRowHeights(index, index);
60520         this.layout();
60521         this.fireEvent("rowupdated", this, index, record);
60522     },
60523
60524     onAdd : function(ds, records, index){
60525         this.insertRows(ds, index, index + (records.length-1));
60526     },
60527
60528     onRemove : function(ds, record, index, isUpdate){
60529         if(isUpdate !== true){
60530             this.fireEvent("beforerowremoved", this, index, record);
60531         }
60532         var bt = this.getBodyTable(), lt = this.getLockedTable();
60533         if(bt.rows[index]){
60534             bt.firstChild.removeChild(bt.rows[index]);
60535         }
60536         if(lt.rows[index]){
60537             lt.firstChild.removeChild(lt.rows[index]);
60538         }
60539         if(isUpdate !== true){
60540             this.stripeRows(index);
60541             this.syncRowHeights(index, index);
60542             this.layout();
60543             this.fireEvent("rowremoved", this, index, record);
60544         }
60545     },
60546
60547     onLoad : function(){
60548         this.scrollToTop();
60549     },
60550
60551     /**
60552      * Scrolls the grid to the top
60553      */
60554     scrollToTop : function(){
60555         if(this.scroller){
60556             this.scroller.dom.scrollTop = 0;
60557             this.syncScroll();
60558         }
60559     },
60560
60561     /**
60562      * Gets a panel in the header of the grid that can be used for toolbars etc.
60563      * After modifying the contents of this panel a call to grid.autoSize() may be
60564      * required to register any changes in size.
60565      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
60566      * @return Roo.Element
60567      */
60568     getHeaderPanel : function(doShow){
60569         if(doShow){
60570             this.headerPanel.show();
60571         }
60572         return this.headerPanel;
60573     },
60574
60575     /**
60576      * Gets a panel in the footer of the grid that can be used for toolbars etc.
60577      * After modifying the contents of this panel a call to grid.autoSize() may be
60578      * required to register any changes in size.
60579      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
60580      * @return Roo.Element
60581      */
60582     getFooterPanel : function(doShow){
60583         if(doShow){
60584             this.footerPanel.show();
60585         }
60586         return this.footerPanel;
60587     },
60588
60589     initElements : function(){
60590         var E = Roo.Element;
60591         var el = this.grid.getGridEl().dom.firstChild;
60592         var cs = el.childNodes;
60593
60594         this.el = new E(el);
60595         
60596          this.focusEl = new E(el.firstChild);
60597         this.focusEl.swallowEvent("click", true);
60598         
60599         this.headerPanel = new E(cs[1]);
60600         this.headerPanel.enableDisplayMode("block");
60601
60602         this.scroller = new E(cs[2]);
60603         this.scrollSizer = new E(this.scroller.dom.firstChild);
60604
60605         this.lockedWrap = new E(cs[3]);
60606         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
60607         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
60608
60609         this.mainWrap = new E(cs[4]);
60610         this.mainHd = new E(this.mainWrap.dom.firstChild);
60611         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
60612
60613         this.footerPanel = new E(cs[5]);
60614         this.footerPanel.enableDisplayMode("block");
60615
60616         this.resizeProxy = new E(cs[6]);
60617
60618         this.headerSelector = String.format(
60619            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
60620            this.lockedHd.id, this.mainHd.id
60621         );
60622
60623         this.splitterSelector = String.format(
60624            '#{0} div.x-grid-split, #{1} div.x-grid-split',
60625            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
60626         );
60627     },
60628     idToCssName : function(s)
60629     {
60630         return s.replace(/[^a-z0-9]+/ig, '-');
60631     },
60632
60633     getHeaderCell : function(index){
60634         return Roo.DomQuery.select(this.headerSelector)[index];
60635     },
60636
60637     getHeaderCellMeasure : function(index){
60638         return this.getHeaderCell(index).firstChild;
60639     },
60640
60641     getHeaderCellText : function(index){
60642         return this.getHeaderCell(index).firstChild.firstChild;
60643     },
60644
60645     getLockedTable : function(){
60646         return this.lockedBody.dom.firstChild;
60647     },
60648
60649     getBodyTable : function(){
60650         return this.mainBody.dom.firstChild;
60651     },
60652
60653     getLockedRow : function(index){
60654         return this.getLockedTable().rows[index];
60655     },
60656
60657     getRow : function(index){
60658         return this.getBodyTable().rows[index];
60659     },
60660
60661     getRowComposite : function(index){
60662         if(!this.rowEl){
60663             this.rowEl = new Roo.CompositeElementLite();
60664         }
60665         var els = [], lrow, mrow;
60666         if(lrow = this.getLockedRow(index)){
60667             els.push(lrow);
60668         }
60669         if(mrow = this.getRow(index)){
60670             els.push(mrow);
60671         }
60672         this.rowEl.elements = els;
60673         return this.rowEl;
60674     },
60675     /**
60676      * Gets the 'td' of the cell
60677      * 
60678      * @param {Integer} rowIndex row to select
60679      * @param {Integer} colIndex column to select
60680      * 
60681      * @return {Object} 
60682      */
60683     getCell : function(rowIndex, colIndex){
60684         var locked = this.cm.getLockedCount();
60685         var source;
60686         if(colIndex < locked){
60687             source = this.lockedBody.dom.firstChild;
60688         }else{
60689             source = this.mainBody.dom.firstChild;
60690             colIndex -= locked;
60691         }
60692         return source.rows[rowIndex].childNodes[colIndex];
60693     },
60694
60695     getCellText : function(rowIndex, colIndex){
60696         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
60697     },
60698
60699     getCellBox : function(cell){
60700         var b = this.fly(cell).getBox();
60701         if(Roo.isOpera){ // opera fails to report the Y
60702             b.y = cell.offsetTop + this.mainBody.getY();
60703         }
60704         return b;
60705     },
60706
60707     getCellIndex : function(cell){
60708         var id = String(cell.className).match(this.cellRE);
60709         if(id){
60710             return parseInt(id[1], 10);
60711         }
60712         return 0;
60713     },
60714
60715     findHeaderIndex : function(n){
60716         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
60717         return r ? this.getCellIndex(r) : false;
60718     },
60719
60720     findHeaderCell : function(n){
60721         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
60722         return r ? r : false;
60723     },
60724
60725     findRowIndex : function(n){
60726         if(!n){
60727             return false;
60728         }
60729         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
60730         return r ? r.rowIndex : false;
60731     },
60732
60733     findCellIndex : function(node){
60734         var stop = this.el.dom;
60735         while(node && node != stop){
60736             if(this.findRE.test(node.className)){
60737                 return this.getCellIndex(node);
60738             }
60739             node = node.parentNode;
60740         }
60741         return false;
60742     },
60743
60744     getColumnId : function(index){
60745         return this.cm.getColumnId(index);
60746     },
60747
60748     getSplitters : function()
60749     {
60750         if(this.splitterSelector){
60751            return Roo.DomQuery.select(this.splitterSelector);
60752         }else{
60753             return null;
60754       }
60755     },
60756
60757     getSplitter : function(index){
60758         return this.getSplitters()[index];
60759     },
60760
60761     onRowOver : function(e, t){
60762         var row;
60763         if((row = this.findRowIndex(t)) !== false){
60764             this.getRowComposite(row).addClass("x-grid-row-over");
60765         }
60766     },
60767
60768     onRowOut : function(e, t){
60769         var row;
60770         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
60771             this.getRowComposite(row).removeClass("x-grid-row-over");
60772         }
60773     },
60774
60775     renderHeaders : function(){
60776         var cm = this.cm;
60777         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
60778         var cb = [], lb = [], sb = [], lsb = [], p = {};
60779         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
60780             p.cellId = "x-grid-hd-0-" + i;
60781             p.splitId = "x-grid-csplit-0-" + i;
60782             p.id = cm.getColumnId(i);
60783             p.value = cm.getColumnHeader(i) || "";
60784             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
60785             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
60786             if(!cm.isLocked(i)){
60787                 cb[cb.length] = ct.apply(p);
60788                 sb[sb.length] = st.apply(p);
60789             }else{
60790                 lb[lb.length] = ct.apply(p);
60791                 lsb[lsb.length] = st.apply(p);
60792             }
60793         }
60794         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
60795                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
60796     },
60797
60798     updateHeaders : function(){
60799         var html = this.renderHeaders();
60800         this.lockedHd.update(html[0]);
60801         this.mainHd.update(html[1]);
60802     },
60803
60804     /**
60805      * Focuses the specified row.
60806      * @param {Number} row The row index
60807      */
60808     focusRow : function(row)
60809     {
60810         //Roo.log('GridView.focusRow');
60811         var x = this.scroller.dom.scrollLeft;
60812         this.focusCell(row, 0, false);
60813         this.scroller.dom.scrollLeft = x;
60814     },
60815
60816     /**
60817      * Focuses the specified cell.
60818      * @param {Number} row The row index
60819      * @param {Number} col The column index
60820      * @param {Boolean} hscroll false to disable horizontal scrolling
60821      */
60822     focusCell : function(row, col, hscroll)
60823     {
60824         //Roo.log('GridView.focusCell');
60825         var el = this.ensureVisible(row, col, hscroll);
60826         this.focusEl.alignTo(el, "tl-tl");
60827         if(Roo.isGecko){
60828             this.focusEl.focus();
60829         }else{
60830             this.focusEl.focus.defer(1, this.focusEl);
60831         }
60832     },
60833
60834     /**
60835      * Scrolls the specified cell into view
60836      * @param {Number} row The row index
60837      * @param {Number} col The column index
60838      * @param {Boolean} hscroll false to disable horizontal scrolling
60839      */
60840     ensureVisible : function(row, col, hscroll)
60841     {
60842         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
60843         //return null; //disable for testing.
60844         if(typeof row != "number"){
60845             row = row.rowIndex;
60846         }
60847         if(row < 0 && row >= this.ds.getCount()){
60848             return  null;
60849         }
60850         col = (col !== undefined ? col : 0);
60851         var cm = this.grid.colModel;
60852         while(cm.isHidden(col)){
60853             col++;
60854         }
60855
60856         var el = this.getCell(row, col);
60857         if(!el){
60858             return null;
60859         }
60860         var c = this.scroller.dom;
60861
60862         var ctop = parseInt(el.offsetTop, 10);
60863         var cleft = parseInt(el.offsetLeft, 10);
60864         var cbot = ctop + el.offsetHeight;
60865         var cright = cleft + el.offsetWidth;
60866         
60867         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
60868         var stop = parseInt(c.scrollTop, 10);
60869         var sleft = parseInt(c.scrollLeft, 10);
60870         var sbot = stop + ch;
60871         var sright = sleft + c.clientWidth;
60872         /*
60873         Roo.log('GridView.ensureVisible:' +
60874                 ' ctop:' + ctop +
60875                 ' c.clientHeight:' + c.clientHeight +
60876                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
60877                 ' stop:' + stop +
60878                 ' cbot:' + cbot +
60879                 ' sbot:' + sbot +
60880                 ' ch:' + ch  
60881                 );
60882         */
60883         if(ctop < stop){
60884             c.scrollTop = ctop;
60885             //Roo.log("set scrolltop to ctop DISABLE?");
60886         }else if(cbot > sbot){
60887             //Roo.log("set scrolltop to cbot-ch");
60888             c.scrollTop = cbot-ch;
60889         }
60890         
60891         if(hscroll !== false){
60892             if(cleft < sleft){
60893                 c.scrollLeft = cleft;
60894             }else if(cright > sright){
60895                 c.scrollLeft = cright-c.clientWidth;
60896             }
60897         }
60898          
60899         return el;
60900     },
60901
60902     updateColumns : function(){
60903         this.grid.stopEditing();
60904         var cm = this.grid.colModel, colIds = this.getColumnIds();
60905         //var totalWidth = cm.getTotalWidth();
60906         var pos = 0;
60907         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
60908             //if(cm.isHidden(i)) continue;
60909             var w = cm.getColumnWidth(i);
60910             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
60911             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
60912         }
60913         this.updateSplitters();
60914     },
60915
60916     generateRules : function(cm){
60917         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
60918         Roo.util.CSS.removeStyleSheet(rulesId);
60919         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
60920             var cid = cm.getColumnId(i);
60921             var align = '';
60922             if(cm.config[i].align){
60923                 align = 'text-align:'+cm.config[i].align+';';
60924             }
60925             var hidden = '';
60926             if(cm.isHidden(i)){
60927                 hidden = 'display:none;';
60928             }
60929             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
60930             ruleBuf.push(
60931                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
60932                     this.hdSelector, cid, " {\n", align, width, "}\n",
60933                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
60934                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
60935         }
60936         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
60937     },
60938
60939     updateSplitters : function(){
60940         var cm = this.cm, s = this.getSplitters();
60941         if(s){ // splitters not created yet
60942             var pos = 0, locked = true;
60943             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
60944                 if(cm.isHidden(i)) {
60945                     continue;
60946                 }
60947                 var w = cm.getColumnWidth(i); // make sure it's a number
60948                 if(!cm.isLocked(i) && locked){
60949                     pos = 0;
60950                     locked = false;
60951                 }
60952                 pos += w;
60953                 s[i].style.left = (pos-this.splitOffset) + "px";
60954             }
60955         }
60956     },
60957
60958     handleHiddenChange : function(colModel, colIndex, hidden){
60959         if(hidden){
60960             this.hideColumn(colIndex);
60961         }else{
60962             this.unhideColumn(colIndex);
60963         }
60964     },
60965
60966     hideColumn : function(colIndex){
60967         var cid = this.getColumnId(colIndex);
60968         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
60969         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
60970         if(Roo.isSafari){
60971             this.updateHeaders();
60972         }
60973         this.updateSplitters();
60974         this.layout();
60975     },
60976
60977     unhideColumn : function(colIndex){
60978         var cid = this.getColumnId(colIndex);
60979         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
60980         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
60981
60982         if(Roo.isSafari){
60983             this.updateHeaders();
60984         }
60985         this.updateSplitters();
60986         this.layout();
60987     },
60988
60989     insertRows : function(dm, firstRow, lastRow, isUpdate){
60990         if(firstRow == 0 && lastRow == dm.getCount()-1){
60991             this.refresh();
60992         }else{
60993             if(!isUpdate){
60994                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
60995             }
60996             var s = this.getScrollState();
60997             var markup = this.renderRows(firstRow, lastRow);
60998             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
60999             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
61000             this.restoreScroll(s);
61001             if(!isUpdate){
61002                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
61003                 this.syncRowHeights(firstRow, lastRow);
61004                 this.stripeRows(firstRow);
61005                 this.layout();
61006             }
61007         }
61008     },
61009
61010     bufferRows : function(markup, target, index){
61011         var before = null, trows = target.rows, tbody = target.tBodies[0];
61012         if(index < trows.length){
61013             before = trows[index];
61014         }
61015         var b = document.createElement("div");
61016         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
61017         var rows = b.firstChild.rows;
61018         for(var i = 0, len = rows.length; i < len; i++){
61019             if(before){
61020                 tbody.insertBefore(rows[0], before);
61021             }else{
61022                 tbody.appendChild(rows[0]);
61023             }
61024         }
61025         b.innerHTML = "";
61026         b = null;
61027     },
61028
61029     deleteRows : function(dm, firstRow, lastRow){
61030         if(dm.getRowCount()<1){
61031             this.fireEvent("beforerefresh", this);
61032             this.mainBody.update("");
61033             this.lockedBody.update("");
61034             this.fireEvent("refresh", this);
61035         }else{
61036             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
61037             var bt = this.getBodyTable();
61038             var tbody = bt.firstChild;
61039             var rows = bt.rows;
61040             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
61041                 tbody.removeChild(rows[firstRow]);
61042             }
61043             this.stripeRows(firstRow);
61044             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
61045         }
61046     },
61047
61048     updateRows : function(dataSource, firstRow, lastRow){
61049         var s = this.getScrollState();
61050         this.refresh();
61051         this.restoreScroll(s);
61052     },
61053
61054     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
61055         if(!noRefresh){
61056            this.refresh();
61057         }
61058         this.updateHeaderSortState();
61059     },
61060
61061     getScrollState : function(){
61062         
61063         var sb = this.scroller.dom;
61064         return {left: sb.scrollLeft, top: sb.scrollTop};
61065     },
61066
61067     stripeRows : function(startRow){
61068         if(!this.grid.stripeRows || this.ds.getCount() < 1){
61069             return;
61070         }
61071         startRow = startRow || 0;
61072         var rows = this.getBodyTable().rows;
61073         var lrows = this.getLockedTable().rows;
61074         var cls = ' x-grid-row-alt ';
61075         for(var i = startRow, len = rows.length; i < len; i++){
61076             var row = rows[i], lrow = lrows[i];
61077             var isAlt = ((i+1) % 2 == 0);
61078             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
61079             if(isAlt == hasAlt){
61080                 continue;
61081             }
61082             if(isAlt){
61083                 row.className += " x-grid-row-alt";
61084             }else{
61085                 row.className = row.className.replace("x-grid-row-alt", "");
61086             }
61087             if(lrow){
61088                 lrow.className = row.className;
61089             }
61090         }
61091     },
61092
61093     restoreScroll : function(state){
61094         //Roo.log('GridView.restoreScroll');
61095         var sb = this.scroller.dom;
61096         sb.scrollLeft = state.left;
61097         sb.scrollTop = state.top;
61098         this.syncScroll();
61099     },
61100
61101     syncScroll : function(){
61102         //Roo.log('GridView.syncScroll');
61103         var sb = this.scroller.dom;
61104         var sh = this.mainHd.dom;
61105         var bs = this.mainBody.dom;
61106         var lv = this.lockedBody.dom;
61107         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
61108         lv.scrollTop = bs.scrollTop = sb.scrollTop;
61109     },
61110
61111     handleScroll : function(e){
61112         this.syncScroll();
61113         var sb = this.scroller.dom;
61114         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
61115         e.stopEvent();
61116     },
61117
61118     handleWheel : function(e){
61119         var d = e.getWheelDelta();
61120         this.scroller.dom.scrollTop -= d*22;
61121         // set this here to prevent jumpy scrolling on large tables
61122         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
61123         e.stopEvent();
61124     },
61125
61126     renderRows : function(startRow, endRow){
61127         // pull in all the crap needed to render rows
61128         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
61129         var colCount = cm.getColumnCount();
61130
61131         if(ds.getCount() < 1){
61132             return ["", ""];
61133         }
61134
61135         // build a map for all the columns
61136         var cs = [];
61137         for(var i = 0; i < colCount; i++){
61138             var name = cm.getDataIndex(i);
61139             cs[i] = {
61140                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
61141                 renderer : cm.getRenderer(i),
61142                 id : cm.getColumnId(i),
61143                 locked : cm.isLocked(i),
61144                 has_editor : cm.isCellEditable(i)
61145             };
61146         }
61147
61148         startRow = startRow || 0;
61149         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
61150
61151         // records to render
61152         var rs = ds.getRange(startRow, endRow);
61153
61154         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
61155     },
61156
61157     // As much as I hate to duplicate code, this was branched because FireFox really hates
61158     // [].join("") on strings. The performance difference was substantial enough to
61159     // branch this function
61160     doRender : Roo.isGecko ?
61161             function(cs, rs, ds, startRow, colCount, stripe){
61162                 var ts = this.templates, ct = ts.cell, rt = ts.row;
61163                 // buffers
61164                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
61165                 
61166                 var hasListener = this.grid.hasListener('rowclass');
61167                 var rowcfg = {};
61168                 for(var j = 0, len = rs.length; j < len; j++){
61169                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
61170                     for(var i = 0; i < colCount; i++){
61171                         c = cs[i];
61172                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
61173                         p.id = c.id;
61174                         p.css = p.attr = "";
61175                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
61176                         if(p.value == undefined || p.value === "") {
61177                             p.value = "&#160;";
61178                         }
61179                         if(c.has_editor){
61180                             p.css += ' x-grid-editable-cell';
61181                         }
61182                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
61183                             p.css +=  ' x-grid-dirty-cell';
61184                         }
61185                         var markup = ct.apply(p);
61186                         if(!c.locked){
61187                             cb+= markup;
61188                         }else{
61189                             lcb+= markup;
61190                         }
61191                     }
61192                     var alt = [];
61193                     if(stripe && ((rowIndex+1) % 2 == 0)){
61194                         alt.push("x-grid-row-alt")
61195                     }
61196                     if(r.dirty){
61197                         alt.push(  " x-grid-dirty-row");
61198                     }
61199                     rp.cells = lcb;
61200                     if(this.getRowClass){
61201                         alt.push(this.getRowClass(r, rowIndex));
61202                     }
61203                     if (hasListener) {
61204                         rowcfg = {
61205                              
61206                             record: r,
61207                             rowIndex : rowIndex,
61208                             rowClass : ''
61209                         };
61210                         this.grid.fireEvent('rowclass', this, rowcfg);
61211                         alt.push(rowcfg.rowClass);
61212                     }
61213                     rp.alt = alt.join(" ");
61214                     lbuf+= rt.apply(rp);
61215                     rp.cells = cb;
61216                     buf+=  rt.apply(rp);
61217                 }
61218                 return [lbuf, buf];
61219             } :
61220             function(cs, rs, ds, startRow, colCount, stripe){
61221                 var ts = this.templates, ct = ts.cell, rt = ts.row;
61222                 // buffers
61223                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
61224                 var hasListener = this.grid.hasListener('rowclass');
61225  
61226                 var rowcfg = {};
61227                 for(var j = 0, len = rs.length; j < len; j++){
61228                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
61229                     for(var i = 0; i < colCount; i++){
61230                         c = cs[i];
61231                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
61232                         p.id = c.id;
61233                         p.css = p.attr = "";
61234                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
61235                         if(p.value == undefined || p.value === "") {
61236                             p.value = "&#160;";
61237                         }
61238                         //Roo.log(c);
61239                          if(c.has_editor){
61240                             p.css += ' x-grid-editable-cell';
61241                         }
61242                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
61243                             p.css += ' x-grid-dirty-cell' 
61244                         }
61245                         
61246                         var markup = ct.apply(p);
61247                         if(!c.locked){
61248                             cb[cb.length] = markup;
61249                         }else{
61250                             lcb[lcb.length] = markup;
61251                         }
61252                     }
61253                     var alt = [];
61254                     if(stripe && ((rowIndex+1) % 2 == 0)){
61255                         alt.push( "x-grid-row-alt");
61256                     }
61257                     if(r.dirty){
61258                         alt.push(" x-grid-dirty-row");
61259                     }
61260                     rp.cells = lcb;
61261                     if(this.getRowClass){
61262                         alt.push( this.getRowClass(r, rowIndex));
61263                     }
61264                     if (hasListener) {
61265                         rowcfg = {
61266                              
61267                             record: r,
61268                             rowIndex : rowIndex,
61269                             rowClass : ''
61270                         };
61271                         this.grid.fireEvent('rowclass', this, rowcfg);
61272                         alt.push(rowcfg.rowClass);
61273                     }
61274                     
61275                     rp.alt = alt.join(" ");
61276                     rp.cells = lcb.join("");
61277                     lbuf[lbuf.length] = rt.apply(rp);
61278                     rp.cells = cb.join("");
61279                     buf[buf.length] =  rt.apply(rp);
61280                 }
61281                 return [lbuf.join(""), buf.join("")];
61282             },
61283
61284     renderBody : function(){
61285         var markup = this.renderRows();
61286         var bt = this.templates.body;
61287         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
61288     },
61289
61290     /**
61291      * Refreshes the grid
61292      * @param {Boolean} headersToo
61293      */
61294     refresh : function(headersToo){
61295         this.fireEvent("beforerefresh", this);
61296         this.grid.stopEditing();
61297         var result = this.renderBody();
61298         this.lockedBody.update(result[0]);
61299         this.mainBody.update(result[1]);
61300         if(headersToo === true){
61301             this.updateHeaders();
61302             this.updateColumns();
61303             this.updateSplitters();
61304             this.updateHeaderSortState();
61305         }
61306         this.syncRowHeights();
61307         this.layout();
61308         this.fireEvent("refresh", this);
61309     },
61310
61311     handleColumnMove : function(cm, oldIndex, newIndex){
61312         this.indexMap = null;
61313         var s = this.getScrollState();
61314         this.refresh(true);
61315         this.restoreScroll(s);
61316         this.afterMove(newIndex);
61317     },
61318
61319     afterMove : function(colIndex){
61320         if(this.enableMoveAnim && Roo.enableFx){
61321             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
61322         }
61323         // if multisort - fix sortOrder, and reload..
61324         if (this.grid.dataSource.multiSort) {
61325             // the we can call sort again..
61326             var dm = this.grid.dataSource;
61327             var cm = this.grid.colModel;
61328             var so = [];
61329             for(var i = 0; i < cm.config.length; i++ ) {
61330                 
61331                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
61332                     continue; // dont' bother, it's not in sort list or being set.
61333                 }
61334                 
61335                 so.push(cm.config[i].dataIndex);
61336             };
61337             dm.sortOrder = so;
61338             dm.load(dm.lastOptions);
61339             
61340             
61341         }
61342         
61343     },
61344
61345     updateCell : function(dm, rowIndex, dataIndex){
61346         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
61347         if(typeof colIndex == "undefined"){ // not present in grid
61348             return;
61349         }
61350         var cm = this.grid.colModel;
61351         var cell = this.getCell(rowIndex, colIndex);
61352         var cellText = this.getCellText(rowIndex, colIndex);
61353
61354         var p = {
61355             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
61356             id : cm.getColumnId(colIndex),
61357             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
61358         };
61359         var renderer = cm.getRenderer(colIndex);
61360         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
61361         if(typeof val == "undefined" || val === "") {
61362             val = "&#160;";
61363         }
61364         cellText.innerHTML = val;
61365         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
61366         this.syncRowHeights(rowIndex, rowIndex);
61367     },
61368
61369     calcColumnWidth : function(colIndex, maxRowsToMeasure){
61370         var maxWidth = 0;
61371         if(this.grid.autoSizeHeaders){
61372             var h = this.getHeaderCellMeasure(colIndex);
61373             maxWidth = Math.max(maxWidth, h.scrollWidth);
61374         }
61375         var tb, index;
61376         if(this.cm.isLocked(colIndex)){
61377             tb = this.getLockedTable();
61378             index = colIndex;
61379         }else{
61380             tb = this.getBodyTable();
61381             index = colIndex - this.cm.getLockedCount();
61382         }
61383         if(tb && tb.rows){
61384             var rows = tb.rows;
61385             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
61386             for(var i = 0; i < stopIndex; i++){
61387                 var cell = rows[i].childNodes[index].firstChild;
61388                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
61389             }
61390         }
61391         return maxWidth + /*margin for error in IE*/ 5;
61392     },
61393     /**
61394      * Autofit a column to its content.
61395      * @param {Number} colIndex
61396      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
61397      */
61398      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
61399          if(this.cm.isHidden(colIndex)){
61400              return; // can't calc a hidden column
61401          }
61402         if(forceMinSize){
61403             var cid = this.cm.getColumnId(colIndex);
61404             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
61405            if(this.grid.autoSizeHeaders){
61406                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
61407            }
61408         }
61409         var newWidth = this.calcColumnWidth(colIndex);
61410         this.cm.setColumnWidth(colIndex,
61411             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
61412         if(!suppressEvent){
61413             this.grid.fireEvent("columnresize", colIndex, newWidth);
61414         }
61415     },
61416
61417     /**
61418      * Autofits all columns to their content and then expands to fit any extra space in the grid
61419      */
61420      autoSizeColumns : function(){
61421         var cm = this.grid.colModel;
61422         var colCount = cm.getColumnCount();
61423         for(var i = 0; i < colCount; i++){
61424             this.autoSizeColumn(i, true, true);
61425         }
61426         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
61427             this.fitColumns();
61428         }else{
61429             this.updateColumns();
61430             this.layout();
61431         }
61432     },
61433
61434     /**
61435      * Autofits all columns to the grid's width proportionate with their current size
61436      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
61437      */
61438     fitColumns : function(reserveScrollSpace){
61439         var cm = this.grid.colModel;
61440         var colCount = cm.getColumnCount();
61441         var cols = [];
61442         var width = 0;
61443         var i, w;
61444         for (i = 0; i < colCount; i++){
61445             if(!cm.isHidden(i) && !cm.isFixed(i)){
61446                 w = cm.getColumnWidth(i);
61447                 cols.push(i);
61448                 cols.push(w);
61449                 width += w;
61450             }
61451         }
61452         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
61453         if(reserveScrollSpace){
61454             avail -= 17;
61455         }
61456         var frac = (avail - cm.getTotalWidth())/width;
61457         while (cols.length){
61458             w = cols.pop();
61459             i = cols.pop();
61460             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
61461         }
61462         this.updateColumns();
61463         this.layout();
61464     },
61465
61466     onRowSelect : function(rowIndex){
61467         var row = this.getRowComposite(rowIndex);
61468         row.addClass("x-grid-row-selected");
61469     },
61470
61471     onRowDeselect : function(rowIndex){
61472         var row = this.getRowComposite(rowIndex);
61473         row.removeClass("x-grid-row-selected");
61474     },
61475
61476     onCellSelect : function(row, col){
61477         var cell = this.getCell(row, col);
61478         if(cell){
61479             Roo.fly(cell).addClass("x-grid-cell-selected");
61480         }
61481     },
61482
61483     onCellDeselect : function(row, col){
61484         var cell = this.getCell(row, col);
61485         if(cell){
61486             Roo.fly(cell).removeClass("x-grid-cell-selected");
61487         }
61488     },
61489
61490     updateHeaderSortState : function(){
61491         
61492         // sort state can be single { field: xxx, direction : yyy}
61493         // or   { xxx=>ASC , yyy : DESC ..... }
61494         
61495         var mstate = {};
61496         if (!this.ds.multiSort) { 
61497             var state = this.ds.getSortState();
61498             if(!state){
61499                 return;
61500             }
61501             mstate[state.field] = state.direction;
61502             // FIXME... - this is not used here.. but might be elsewhere..
61503             this.sortState = state;
61504             
61505         } else {
61506             mstate = this.ds.sortToggle;
61507         }
61508         //remove existing sort classes..
61509         
61510         var sc = this.sortClasses;
61511         var hds = this.el.select(this.headerSelector).removeClass(sc);
61512         
61513         for(var f in mstate) {
61514         
61515             var sortColumn = this.cm.findColumnIndex(f);
61516             
61517             if(sortColumn != -1){
61518                 var sortDir = mstate[f];        
61519                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
61520             }
61521         }
61522         
61523          
61524         
61525     },
61526
61527
61528     handleHeaderClick : function(g, index,e){
61529         
61530         Roo.log("header click");
61531         
61532         if (Roo.isTouch) {
61533             // touch events on header are handled by context
61534             this.handleHdCtx(g,index,e);
61535             return;
61536         }
61537         
61538         
61539         if(this.headersDisabled){
61540             return;
61541         }
61542         var dm = g.dataSource, cm = g.colModel;
61543         if(!cm.isSortable(index)){
61544             return;
61545         }
61546         g.stopEditing();
61547         
61548         if (dm.multiSort) {
61549             // update the sortOrder
61550             var so = [];
61551             for(var i = 0; i < cm.config.length; i++ ) {
61552                 
61553                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
61554                     continue; // dont' bother, it's not in sort list or being set.
61555                 }
61556                 
61557                 so.push(cm.config[i].dataIndex);
61558             };
61559             dm.sortOrder = so;
61560         }
61561         
61562         
61563         dm.sort(cm.getDataIndex(index));
61564     },
61565
61566
61567     destroy : function(){
61568         if(this.colMenu){
61569             this.colMenu.removeAll();
61570             Roo.menu.MenuMgr.unregister(this.colMenu);
61571             this.colMenu.getEl().remove();
61572             delete this.colMenu;
61573         }
61574         if(this.hmenu){
61575             this.hmenu.removeAll();
61576             Roo.menu.MenuMgr.unregister(this.hmenu);
61577             this.hmenu.getEl().remove();
61578             delete this.hmenu;
61579         }
61580         if(this.grid.enableColumnMove){
61581             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
61582             if(dds){
61583                 for(var dd in dds){
61584                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
61585                         var elid = dds[dd].dragElId;
61586                         dds[dd].unreg();
61587                         Roo.get(elid).remove();
61588                     } else if(dds[dd].config.isTarget){
61589                         dds[dd].proxyTop.remove();
61590                         dds[dd].proxyBottom.remove();
61591                         dds[dd].unreg();
61592                     }
61593                     if(Roo.dd.DDM.locationCache[dd]){
61594                         delete Roo.dd.DDM.locationCache[dd];
61595                     }
61596                 }
61597                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
61598             }
61599         }
61600         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
61601         this.bind(null, null);
61602         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
61603     },
61604
61605     handleLockChange : function(){
61606         this.refresh(true);
61607     },
61608
61609     onDenyColumnLock : function(){
61610
61611     },
61612
61613     onDenyColumnHide : function(){
61614
61615     },
61616
61617     handleHdMenuClick : function(item){
61618         var index = this.hdCtxIndex;
61619         var cm = this.cm, ds = this.ds;
61620         switch(item.id){
61621             case "asc":
61622                 ds.sort(cm.getDataIndex(index), "ASC");
61623                 break;
61624             case "desc":
61625                 ds.sort(cm.getDataIndex(index), "DESC");
61626                 break;
61627             case "lock":
61628                 var lc = cm.getLockedCount();
61629                 if(cm.getColumnCount(true) <= lc+1){
61630                     this.onDenyColumnLock();
61631                     return;
61632                 }
61633                 if(lc != index){
61634                     cm.setLocked(index, true, true);
61635                     cm.moveColumn(index, lc);
61636                     this.grid.fireEvent("columnmove", index, lc);
61637                 }else{
61638                     cm.setLocked(index, true);
61639                 }
61640             break;
61641             case "unlock":
61642                 var lc = cm.getLockedCount();
61643                 if((lc-1) != index){
61644                     cm.setLocked(index, false, true);
61645                     cm.moveColumn(index, lc-1);
61646                     this.grid.fireEvent("columnmove", index, lc-1);
61647                 }else{
61648                     cm.setLocked(index, false);
61649                 }
61650             break;
61651             case 'wider': // used to expand cols on touch..
61652             case 'narrow':
61653                 var cw = cm.getColumnWidth(index);
61654                 cw += (item.id == 'wider' ? 1 : -1) * 50;
61655                 cw = Math.max(0, cw);
61656                 cw = Math.min(cw,4000);
61657                 cm.setColumnWidth(index, cw);
61658                 break;
61659                 
61660             default:
61661                 index = cm.getIndexById(item.id.substr(4));
61662                 if(index != -1){
61663                     if(item.checked && cm.getColumnCount(true) <= 1){
61664                         this.onDenyColumnHide();
61665                         return false;
61666                     }
61667                     cm.setHidden(index, item.checked);
61668                 }
61669         }
61670         return true;
61671     },
61672
61673     beforeColMenuShow : function(){
61674         var cm = this.cm,  colCount = cm.getColumnCount();
61675         this.colMenu.removeAll();
61676         
61677         var items = [];
61678         for(var i = 0; i < colCount; i++){
61679             items.push({
61680                 id: "col-"+cm.getColumnId(i),
61681                 text: cm.getColumnHeader(i),
61682                 checked: !cm.isHidden(i),
61683                 hideOnClick:false
61684             });
61685         }
61686         
61687         if (this.grid.sortColMenu) {
61688             items.sort(function(a,b) {
61689                 if (a.text == b.text) {
61690                     return 0;
61691                 }
61692                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
61693             });
61694         }
61695         
61696         for(var i = 0; i < colCount; i++){
61697             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
61698         }
61699     },
61700
61701     handleHdCtx : function(g, index, e){
61702         e.stopEvent();
61703         var hd = this.getHeaderCell(index);
61704         this.hdCtxIndex = index;
61705         var ms = this.hmenu.items, cm = this.cm;
61706         ms.get("asc").setDisabled(!cm.isSortable(index));
61707         ms.get("desc").setDisabled(!cm.isSortable(index));
61708         if(this.grid.enableColLock !== false){
61709             ms.get("lock").setDisabled(cm.isLocked(index));
61710             ms.get("unlock").setDisabled(!cm.isLocked(index));
61711         }
61712         this.hmenu.show(hd, "tl-bl");
61713     },
61714
61715     handleHdOver : function(e){
61716         var hd = this.findHeaderCell(e.getTarget());
61717         if(hd && !this.headersDisabled){
61718             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
61719                this.fly(hd).addClass("x-grid-hd-over");
61720             }
61721         }
61722     },
61723
61724     handleHdOut : function(e){
61725         var hd = this.findHeaderCell(e.getTarget());
61726         if(hd){
61727             this.fly(hd).removeClass("x-grid-hd-over");
61728         }
61729     },
61730
61731     handleSplitDblClick : function(e, t){
61732         var i = this.getCellIndex(t);
61733         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
61734             this.autoSizeColumn(i, true);
61735             this.layout();
61736         }
61737     },
61738
61739     render : function(){
61740
61741         var cm = this.cm;
61742         var colCount = cm.getColumnCount();
61743
61744         if(this.grid.monitorWindowResize === true){
61745             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
61746         }
61747         var header = this.renderHeaders();
61748         var body = this.templates.body.apply({rows:""});
61749         var html = this.templates.master.apply({
61750             lockedBody: body,
61751             body: body,
61752             lockedHeader: header[0],
61753             header: header[1]
61754         });
61755
61756         //this.updateColumns();
61757
61758         this.grid.getGridEl().dom.innerHTML = html;
61759
61760         this.initElements();
61761         
61762         // a kludge to fix the random scolling effect in webkit
61763         this.el.on("scroll", function() {
61764             this.el.dom.scrollTop=0; // hopefully not recursive..
61765         },this);
61766
61767         this.scroller.on("scroll", this.handleScroll, this);
61768         this.lockedBody.on("mousewheel", this.handleWheel, this);
61769         this.mainBody.on("mousewheel", this.handleWheel, this);
61770
61771         this.mainHd.on("mouseover", this.handleHdOver, this);
61772         this.mainHd.on("mouseout", this.handleHdOut, this);
61773         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
61774                 {delegate: "."+this.splitClass});
61775
61776         this.lockedHd.on("mouseover", this.handleHdOver, this);
61777         this.lockedHd.on("mouseout", this.handleHdOut, this);
61778         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
61779                 {delegate: "."+this.splitClass});
61780
61781         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
61782             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
61783         }
61784
61785         this.updateSplitters();
61786
61787         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
61788             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
61789             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
61790         }
61791
61792         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
61793             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
61794             this.hmenu.add(
61795                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
61796                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
61797             );
61798             if(this.grid.enableColLock !== false){
61799                 this.hmenu.add('-',
61800                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
61801                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
61802                 );
61803             }
61804             if (Roo.isTouch) {
61805                  this.hmenu.add('-',
61806                     {id:"wider", text: this.columnsWiderText},
61807                     {id:"narrow", text: this.columnsNarrowText }
61808                 );
61809                 
61810                  
61811             }
61812             
61813             if(this.grid.enableColumnHide !== false){
61814
61815                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
61816                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
61817                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
61818
61819                 this.hmenu.add('-',
61820                     {id:"columns", text: this.columnsText, menu: this.colMenu}
61821                 );
61822             }
61823             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
61824
61825             this.grid.on("headercontextmenu", this.handleHdCtx, this);
61826         }
61827
61828         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
61829             this.dd = new Roo.grid.GridDragZone(this.grid, {
61830                 ddGroup : this.grid.ddGroup || 'GridDD'
61831             });
61832             
61833         }
61834
61835         /*
61836         for(var i = 0; i < colCount; i++){
61837             if(cm.isHidden(i)){
61838                 this.hideColumn(i);
61839             }
61840             if(cm.config[i].align){
61841                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
61842                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
61843             }
61844         }*/
61845         
61846         this.updateHeaderSortState();
61847
61848         this.beforeInitialResize();
61849         this.layout(true);
61850
61851         // two part rendering gives faster view to the user
61852         this.renderPhase2.defer(1, this);
61853     },
61854
61855     renderPhase2 : function(){
61856         // render the rows now
61857         this.refresh();
61858         if(this.grid.autoSizeColumns){
61859             this.autoSizeColumns();
61860         }
61861     },
61862
61863     beforeInitialResize : function(){
61864
61865     },
61866
61867     onColumnSplitterMoved : function(i, w){
61868         this.userResized = true;
61869         var cm = this.grid.colModel;
61870         cm.setColumnWidth(i, w, true);
61871         var cid = cm.getColumnId(i);
61872         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
61873         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
61874         this.updateSplitters();
61875         this.layout();
61876         this.grid.fireEvent("columnresize", i, w);
61877     },
61878
61879     syncRowHeights : function(startIndex, endIndex){
61880         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
61881             startIndex = startIndex || 0;
61882             var mrows = this.getBodyTable().rows;
61883             var lrows = this.getLockedTable().rows;
61884             var len = mrows.length-1;
61885             endIndex = Math.min(endIndex || len, len);
61886             for(var i = startIndex; i <= endIndex; i++){
61887                 var m = mrows[i], l = lrows[i];
61888                 var h = Math.max(m.offsetHeight, l.offsetHeight);
61889                 m.style.height = l.style.height = h + "px";
61890             }
61891         }
61892     },
61893
61894     layout : function(initialRender, is2ndPass)
61895     {
61896         var g = this.grid;
61897         var auto = g.autoHeight;
61898         var scrollOffset = 16;
61899         var c = g.getGridEl(), cm = this.cm,
61900                 expandCol = g.autoExpandColumn,
61901                 gv = this;
61902         //c.beginMeasure();
61903
61904         if(!c.dom.offsetWidth){ // display:none?
61905             if(initialRender){
61906                 this.lockedWrap.show();
61907                 this.mainWrap.show();
61908             }
61909             return;
61910         }
61911
61912         var hasLock = this.cm.isLocked(0);
61913
61914         var tbh = this.headerPanel.getHeight();
61915         var bbh = this.footerPanel.getHeight();
61916
61917         if(auto){
61918             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
61919             var newHeight = ch + c.getBorderWidth("tb");
61920             if(g.maxHeight){
61921                 newHeight = Math.min(g.maxHeight, newHeight);
61922             }
61923             c.setHeight(newHeight);
61924         }
61925
61926         if(g.autoWidth){
61927             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
61928         }
61929
61930         var s = this.scroller;
61931
61932         var csize = c.getSize(true);
61933
61934         this.el.setSize(csize.width, csize.height);
61935
61936         this.headerPanel.setWidth(csize.width);
61937         this.footerPanel.setWidth(csize.width);
61938
61939         var hdHeight = this.mainHd.getHeight();
61940         var vw = csize.width;
61941         var vh = csize.height - (tbh + bbh);
61942
61943         s.setSize(vw, vh);
61944
61945         var bt = this.getBodyTable();
61946         
61947         if(cm.getLockedCount() == cm.config.length){
61948             bt = this.getLockedTable();
61949         }
61950         
61951         var ltWidth = hasLock ?
61952                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
61953
61954         var scrollHeight = bt.offsetHeight;
61955         var scrollWidth = ltWidth + bt.offsetWidth;
61956         var vscroll = false, hscroll = false;
61957
61958         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
61959
61960         var lw = this.lockedWrap, mw = this.mainWrap;
61961         var lb = this.lockedBody, mb = this.mainBody;
61962
61963         setTimeout(function(){
61964             var t = s.dom.offsetTop;
61965             var w = s.dom.clientWidth,
61966                 h = s.dom.clientHeight;
61967
61968             lw.setTop(t);
61969             lw.setSize(ltWidth, h);
61970
61971             mw.setLeftTop(ltWidth, t);
61972             mw.setSize(w-ltWidth, h);
61973
61974             lb.setHeight(h-hdHeight);
61975             mb.setHeight(h-hdHeight);
61976
61977             if(is2ndPass !== true && !gv.userResized && expandCol){
61978                 // high speed resize without full column calculation
61979                 
61980                 var ci = cm.getIndexById(expandCol);
61981                 if (ci < 0) {
61982                     ci = cm.findColumnIndex(expandCol);
61983                 }
61984                 ci = Math.max(0, ci); // make sure it's got at least the first col.
61985                 var expandId = cm.getColumnId(ci);
61986                 var  tw = cm.getTotalWidth(false);
61987                 var currentWidth = cm.getColumnWidth(ci);
61988                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
61989                 if(currentWidth != cw){
61990                     cm.setColumnWidth(ci, cw, true);
61991                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
61992                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
61993                     gv.updateSplitters();
61994                     gv.layout(false, true);
61995                 }
61996             }
61997
61998             if(initialRender){
61999                 lw.show();
62000                 mw.show();
62001             }
62002             //c.endMeasure();
62003         }, 10);
62004     },
62005
62006     onWindowResize : function(){
62007         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
62008             return;
62009         }
62010         this.layout();
62011     },
62012
62013     appendFooter : function(parentEl){
62014         return null;
62015     },
62016
62017     sortAscText : "Sort Ascending",
62018     sortDescText : "Sort Descending",
62019     lockText : "Lock Column",
62020     unlockText : "Unlock Column",
62021     columnsText : "Columns",
62022  
62023     columnsWiderText : "Wider",
62024     columnsNarrowText : "Thinner"
62025 });
62026
62027
62028 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
62029     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
62030     this.proxy.el.addClass('x-grid3-col-dd');
62031 };
62032
62033 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
62034     handleMouseDown : function(e){
62035
62036     },
62037
62038     callHandleMouseDown : function(e){
62039         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
62040     }
62041 });
62042 /*
62043  * Based on:
62044  * Ext JS Library 1.1.1
62045  * Copyright(c) 2006-2007, Ext JS, LLC.
62046  *
62047  * Originally Released Under LGPL - original licence link has changed is not relivant.
62048  *
62049  * Fork - LGPL
62050  * <script type="text/javascript">
62051  */
62052  /**
62053  * @extends Roo.dd.DDProxy
62054  * @class Roo.grid.SplitDragZone
62055  * Support for Column Header resizing
62056  * @constructor
62057  * @param {Object} config
62058  */
62059 // private
62060 // This is a support class used internally by the Grid components
62061 Roo.grid.SplitDragZone = function(grid, hd, hd2){
62062     this.grid = grid;
62063     this.view = grid.getView();
62064     this.proxy = this.view.resizeProxy;
62065     Roo.grid.SplitDragZone.superclass.constructor.call(
62066         this,
62067         hd, // ID
62068         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
62069         {  // CONFIG
62070             dragElId : Roo.id(this.proxy.dom),
62071             resizeFrame:false
62072         }
62073     );
62074     
62075     this.setHandleElId(Roo.id(hd));
62076     if (hd2 !== false) {
62077         this.setOuterHandleElId(Roo.id(hd2));
62078     }
62079     
62080     this.scroll = false;
62081 };
62082 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
62083     fly: Roo.Element.fly,
62084
62085     b4StartDrag : function(x, y){
62086         this.view.headersDisabled = true;
62087         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
62088                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
62089         );
62090         this.proxy.setHeight(h);
62091         
62092         // for old system colWidth really stored the actual width?
62093         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
62094         // which in reality did not work.. - it worked only for fixed sizes
62095         // for resizable we need to use actual sizes.
62096         var w = this.cm.getColumnWidth(this.cellIndex);
62097         if (!this.view.mainWrap) {
62098             // bootstrap.
62099             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
62100         }
62101         
62102         
62103         
62104         // this was w-this.grid.minColumnWidth;
62105         // doesnt really make sense? - w = thie curren width or the rendered one?
62106         var minw = Math.max(w-this.grid.minColumnWidth, 0);
62107         this.resetConstraints();
62108         this.setXConstraint(minw, 1000);
62109         this.setYConstraint(0, 0);
62110         this.minX = x - minw;
62111         this.maxX = x + 1000;
62112         this.startPos = x;
62113         if (!this.view.mainWrap) { // this is Bootstrap code..
62114             this.getDragEl().style.display='block';
62115         }
62116         
62117         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
62118     },
62119
62120
62121     handleMouseDown : function(e){
62122         ev = Roo.EventObject.setEvent(e);
62123         var t = this.fly(ev.getTarget());
62124         if(t.hasClass("x-grid-split")){
62125             this.cellIndex = this.view.getCellIndex(t.dom);
62126             this.split = t.dom;
62127             this.cm = this.grid.colModel;
62128             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
62129                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
62130             }
62131         }
62132     },
62133
62134     endDrag : function(e){
62135         this.view.headersDisabled = false;
62136         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
62137         var diff = endX - this.startPos;
62138         // 
62139         var w = this.cm.getColumnWidth(this.cellIndex);
62140         if (!this.view.mainWrap) {
62141             w = 0;
62142         }
62143         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
62144     },
62145
62146     autoOffset : function(){
62147         this.setDelta(0,0);
62148     }
62149 });/*
62150  * Based on:
62151  * Ext JS Library 1.1.1
62152  * Copyright(c) 2006-2007, Ext JS, LLC.
62153  *
62154  * Originally Released Under LGPL - original licence link has changed is not relivant.
62155  *
62156  * Fork - LGPL
62157  * <script type="text/javascript">
62158  */
62159  
62160 // private
62161 // This is a support class used internally by the Grid components
62162 Roo.grid.GridDragZone = function(grid, config){
62163     this.view = grid.getView();
62164     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
62165     if(this.view.lockedBody){
62166         this.setHandleElId(Roo.id(this.view.mainBody.dom));
62167         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
62168     }
62169     this.scroll = false;
62170     this.grid = grid;
62171     this.ddel = document.createElement('div');
62172     this.ddel.className = 'x-grid-dd-wrap';
62173 };
62174
62175 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
62176     ddGroup : "GridDD",
62177
62178     getDragData : function(e){
62179         var t = Roo.lib.Event.getTarget(e);
62180         var rowIndex = this.view.findRowIndex(t);
62181         var sm = this.grid.selModel;
62182             
62183         //Roo.log(rowIndex);
62184         
62185         if (sm.getSelectedCell) {
62186             // cell selection..
62187             if (!sm.getSelectedCell()) {
62188                 return false;
62189             }
62190             if (rowIndex != sm.getSelectedCell()[0]) {
62191                 return false;
62192             }
62193         
62194         }
62195         if (sm.getSelections && sm.getSelections().length < 1) {
62196             return false;
62197         }
62198         
62199         
62200         // before it used to all dragging of unseleted... - now we dont do that.
62201         if(rowIndex !== false){
62202             
62203             // if editorgrid.. 
62204             
62205             
62206             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
62207                
62208             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
62209               //  
62210             //}
62211             if (e.hasModifier()){
62212                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
62213             }
62214             
62215             Roo.log("getDragData");
62216             
62217             return {
62218                 grid: this.grid,
62219                 ddel: this.ddel,
62220                 rowIndex: rowIndex,
62221                 selections: sm.getSelections ? sm.getSelections() : (
62222                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
62223             };
62224         }
62225         return false;
62226     },
62227     
62228     
62229     onInitDrag : function(e){
62230         var data = this.dragData;
62231         this.ddel.innerHTML = this.grid.getDragDropText();
62232         this.proxy.update(this.ddel);
62233         // fire start drag?
62234     },
62235
62236     afterRepair : function(){
62237         this.dragging = false;
62238     },
62239
62240     getRepairXY : function(e, data){
62241         return false;
62242     },
62243
62244     onEndDrag : function(data, e){
62245         // fire end drag?
62246     },
62247
62248     onValidDrop : function(dd, e, id){
62249         // fire drag drop?
62250         this.hideProxy();
62251     },
62252
62253     beforeInvalidDrop : function(e, id){
62254
62255     }
62256 });/*
62257  * Based on:
62258  * Ext JS Library 1.1.1
62259  * Copyright(c) 2006-2007, Ext JS, LLC.
62260  *
62261  * Originally Released Under LGPL - original licence link has changed is not relivant.
62262  *
62263  * Fork - LGPL
62264  * <script type="text/javascript">
62265  */
62266  
62267
62268 /**
62269  * @class Roo.grid.ColumnModel
62270  * @extends Roo.util.Observable
62271  * This is the default implementation of a ColumnModel used by the Grid. It defines
62272  * the columns in the grid.
62273  * <br>Usage:<br>
62274  <pre><code>
62275  var colModel = new Roo.grid.ColumnModel([
62276         {header: "Ticker", width: 60, sortable: true, locked: true},
62277         {header: "Company Name", width: 150, sortable: true},
62278         {header: "Market Cap.", width: 100, sortable: true},
62279         {header: "$ Sales", width: 100, sortable: true, renderer: money},
62280         {header: "Employees", width: 100, sortable: true, resizable: false}
62281  ]);
62282  </code></pre>
62283  * <p>
62284  
62285  * The config options listed for this class are options which may appear in each
62286  * individual column definition.
62287  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
62288  * @constructor
62289  * @param {Object} config An Array of column config objects. See this class's
62290  * config objects for details.
62291 */
62292 Roo.grid.ColumnModel = function(config){
62293         /**
62294      * The config passed into the constructor
62295      */
62296     this.config = []; //config;
62297     this.lookup = {};
62298
62299     // if no id, create one
62300     // if the column does not have a dataIndex mapping,
62301     // map it to the order it is in the config
62302     for(var i = 0, len = config.length; i < len; i++){
62303         this.addColumn(config[i]);
62304         
62305     }
62306
62307     /**
62308      * The width of columns which have no width specified (defaults to 100)
62309      * @type Number
62310      */
62311     this.defaultWidth = 100;
62312
62313     /**
62314      * Default sortable of columns which have no sortable specified (defaults to false)
62315      * @type Boolean
62316      */
62317     this.defaultSortable = false;
62318
62319     this.addEvents({
62320         /**
62321              * @event widthchange
62322              * Fires when the width of a column changes.
62323              * @param {ColumnModel} this
62324              * @param {Number} columnIndex The column index
62325              * @param {Number} newWidth The new width
62326              */
62327             "widthchange": true,
62328         /**
62329              * @event headerchange
62330              * Fires when the text of a header changes.
62331              * @param {ColumnModel} this
62332              * @param {Number} columnIndex The column index
62333              * @param {Number} newText The new header text
62334              */
62335             "headerchange": true,
62336         /**
62337              * @event hiddenchange
62338              * Fires when a column is hidden or "unhidden".
62339              * @param {ColumnModel} this
62340              * @param {Number} columnIndex The column index
62341              * @param {Boolean} hidden true if hidden, false otherwise
62342              */
62343             "hiddenchange": true,
62344             /**
62345          * @event columnmoved
62346          * Fires when a column is moved.
62347          * @param {ColumnModel} this
62348          * @param {Number} oldIndex
62349          * @param {Number} newIndex
62350          */
62351         "columnmoved" : true,
62352         /**
62353          * @event columlockchange
62354          * Fires when a column's locked state is changed
62355          * @param {ColumnModel} this
62356          * @param {Number} colIndex
62357          * @param {Boolean} locked true if locked
62358          */
62359         "columnlockchange" : true
62360     });
62361     Roo.grid.ColumnModel.superclass.constructor.call(this);
62362 };
62363 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
62364     /**
62365      * @cfg {String} header The header text to display in the Grid view.
62366      */
62367         /**
62368      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
62369      */
62370         /**
62371      * @cfg {String} smHeader Header at Bootsrap Small width
62372      */
62373         /**
62374      * @cfg {String} mdHeader Header at Bootsrap Medium width
62375      */
62376         /**
62377      * @cfg {String} lgHeader Header at Bootsrap Large width
62378      */
62379         /**
62380      * @cfg {String} xlHeader Header at Bootsrap extra Large width
62381      */
62382     /**
62383      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
62384      * {@link Roo.data.Record} definition from which to draw the column's value. If not
62385      * specified, the column's index is used as an index into the Record's data Array.
62386      */
62387     /**
62388      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
62389      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
62390      */
62391     /**
62392      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
62393      * Defaults to the value of the {@link #defaultSortable} property.
62394      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
62395      */
62396     /**
62397      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
62398      */
62399     /**
62400      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
62401      */
62402     /**
62403      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
62404      */
62405     /**
62406      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
62407      */
62408     /**
62409      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
62410      * given the cell's data value. See {@link #setRenderer}. If not specified, the
62411      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
62412      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
62413      */
62414        /**
62415      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
62416      */
62417     /**
62418      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
62419      */
62420     /**
62421      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
62422      */
62423     /**
62424      * @cfg {String} cursor (Optional)
62425      */
62426     /**
62427      * @cfg {String} tooltip (Optional)
62428      */
62429     /**
62430      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
62431      */
62432     /**
62433      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
62434      */
62435     /**
62436      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
62437      */
62438     /**
62439      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
62440      */
62441         /**
62442      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
62443      */
62444     /**
62445      * Returns the id of the column at the specified index.
62446      * @param {Number} index The column index
62447      * @return {String} the id
62448      */
62449     getColumnId : function(index){
62450         return this.config[index].id;
62451     },
62452
62453     /**
62454      * Returns the column for a specified id.
62455      * @param {String} id The column id
62456      * @return {Object} the column
62457      */
62458     getColumnById : function(id){
62459         return this.lookup[id];
62460     },
62461
62462     
62463     /**
62464      * Returns the column Object for a specified dataIndex.
62465      * @param {String} dataIndex The column dataIndex
62466      * @return {Object|Boolean} the column or false if not found
62467      */
62468     getColumnByDataIndex: function(dataIndex){
62469         var index = this.findColumnIndex(dataIndex);
62470         return index > -1 ? this.config[index] : false;
62471     },
62472     
62473     /**
62474      * Returns the index for a specified column id.
62475      * @param {String} id The column id
62476      * @return {Number} the index, or -1 if not found
62477      */
62478     getIndexById : function(id){
62479         for(var i = 0, len = this.config.length; i < len; i++){
62480             if(this.config[i].id == id){
62481                 return i;
62482             }
62483         }
62484         return -1;
62485     },
62486     
62487     /**
62488      * Returns the index for a specified column dataIndex.
62489      * @param {String} dataIndex The column dataIndex
62490      * @return {Number} the index, or -1 if not found
62491      */
62492     
62493     findColumnIndex : function(dataIndex){
62494         for(var i = 0, len = this.config.length; i < len; i++){
62495             if(this.config[i].dataIndex == dataIndex){
62496                 return i;
62497             }
62498         }
62499         return -1;
62500     },
62501     
62502     
62503     moveColumn : function(oldIndex, newIndex){
62504         var c = this.config[oldIndex];
62505         this.config.splice(oldIndex, 1);
62506         this.config.splice(newIndex, 0, c);
62507         this.dataMap = null;
62508         this.fireEvent("columnmoved", this, oldIndex, newIndex);
62509     },
62510
62511     isLocked : function(colIndex){
62512         return this.config[colIndex].locked === true;
62513     },
62514
62515     setLocked : function(colIndex, value, suppressEvent){
62516         if(this.isLocked(colIndex) == value){
62517             return;
62518         }
62519         this.config[colIndex].locked = value;
62520         if(!suppressEvent){
62521             this.fireEvent("columnlockchange", this, colIndex, value);
62522         }
62523     },
62524
62525     getTotalLockedWidth : function(){
62526         var totalWidth = 0;
62527         for(var i = 0; i < this.config.length; i++){
62528             if(this.isLocked(i) && !this.isHidden(i)){
62529                 this.totalWidth += this.getColumnWidth(i);
62530             }
62531         }
62532         return totalWidth;
62533     },
62534
62535     getLockedCount : function(){
62536         for(var i = 0, len = this.config.length; i < len; i++){
62537             if(!this.isLocked(i)){
62538                 return i;
62539             }
62540         }
62541         
62542         return this.config.length;
62543     },
62544
62545     /**
62546      * Returns the number of columns.
62547      * @return {Number}
62548      */
62549     getColumnCount : function(visibleOnly){
62550         if(visibleOnly === true){
62551             var c = 0;
62552             for(var i = 0, len = this.config.length; i < len; i++){
62553                 if(!this.isHidden(i)){
62554                     c++;
62555                 }
62556             }
62557             return c;
62558         }
62559         return this.config.length;
62560     },
62561
62562     /**
62563      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
62564      * @param {Function} fn
62565      * @param {Object} scope (optional)
62566      * @return {Array} result
62567      */
62568     getColumnsBy : function(fn, scope){
62569         var r = [];
62570         for(var i = 0, len = this.config.length; i < len; i++){
62571             var c = this.config[i];
62572             if(fn.call(scope||this, c, i) === true){
62573                 r[r.length] = c;
62574             }
62575         }
62576         return r;
62577     },
62578
62579     /**
62580      * Returns true if the specified column is sortable.
62581      * @param {Number} col The column index
62582      * @return {Boolean}
62583      */
62584     isSortable : function(col){
62585         if(typeof this.config[col].sortable == "undefined"){
62586             return this.defaultSortable;
62587         }
62588         return this.config[col].sortable;
62589     },
62590
62591     /**
62592      * Returns the rendering (formatting) function defined for the column.
62593      * @param {Number} col The column index.
62594      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
62595      */
62596     getRenderer : function(col){
62597         if(!this.config[col].renderer){
62598             return Roo.grid.ColumnModel.defaultRenderer;
62599         }
62600         return this.config[col].renderer;
62601     },
62602
62603     /**
62604      * Sets the rendering (formatting) function for a column.
62605      * @param {Number} col The column index
62606      * @param {Function} fn The function to use to process the cell's raw data
62607      * to return HTML markup for the grid view. The render function is called with
62608      * the following parameters:<ul>
62609      * <li>Data value.</li>
62610      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
62611      * <li>css A CSS style string to apply to the table cell.</li>
62612      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
62613      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
62614      * <li>Row index</li>
62615      * <li>Column index</li>
62616      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
62617      */
62618     setRenderer : function(col, fn){
62619         this.config[col].renderer = fn;
62620     },
62621
62622     /**
62623      * Returns the width for the specified column.
62624      * @param {Number} col The column index
62625      * @param (optional) {String} gridSize bootstrap width size.
62626      * @return {Number}
62627      */
62628     getColumnWidth : function(col, gridSize)
62629         {
62630                 var cfg = this.config[col];
62631                 
62632                 if (typeof(gridSize) == 'undefined') {
62633                         return cfg.width * 1 || this.defaultWidth;
62634                 }
62635                 if (gridSize === false) { // if we set it..
62636                         return cfg.width || false;
62637                 }
62638                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
62639                 
62640                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
62641                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
62642                                 continue;
62643                         }
62644                         return cfg[ sizes[i] ];
62645                 }
62646                 return 1;
62647                 
62648     },
62649
62650     /**
62651      * Sets the width for a column.
62652      * @param {Number} col The column index
62653      * @param {Number} width The new width
62654      */
62655     setColumnWidth : function(col, width, suppressEvent){
62656         this.config[col].width = width;
62657         this.totalWidth = null;
62658         if(!suppressEvent){
62659              this.fireEvent("widthchange", this, col, width);
62660         }
62661     },
62662
62663     /**
62664      * Returns the total width of all columns.
62665      * @param {Boolean} includeHidden True to include hidden column widths
62666      * @return {Number}
62667      */
62668     getTotalWidth : function(includeHidden){
62669         if(!this.totalWidth){
62670             this.totalWidth = 0;
62671             for(var i = 0, len = this.config.length; i < len; i++){
62672                 if(includeHidden || !this.isHidden(i)){
62673                     this.totalWidth += this.getColumnWidth(i);
62674                 }
62675             }
62676         }
62677         return this.totalWidth;
62678     },
62679
62680     /**
62681      * Returns the header for the specified column.
62682      * @param {Number} col The column index
62683      * @return {String}
62684      */
62685     getColumnHeader : function(col){
62686         return this.config[col].header;
62687     },
62688
62689     /**
62690      * Sets the header for a column.
62691      * @param {Number} col The column index
62692      * @param {String} header The new header
62693      */
62694     setColumnHeader : function(col, header){
62695         this.config[col].header = header;
62696         this.fireEvent("headerchange", this, col, header);
62697     },
62698
62699     /**
62700      * Returns the tooltip for the specified column.
62701      * @param {Number} col The column index
62702      * @return {String}
62703      */
62704     getColumnTooltip : function(col){
62705             return this.config[col].tooltip;
62706     },
62707     /**
62708      * Sets the tooltip for a column.
62709      * @param {Number} col The column index
62710      * @param {String} tooltip The new tooltip
62711      */
62712     setColumnTooltip : function(col, tooltip){
62713             this.config[col].tooltip = tooltip;
62714     },
62715
62716     /**
62717      * Returns the dataIndex for the specified column.
62718      * @param {Number} col The column index
62719      * @return {Number}
62720      */
62721     getDataIndex : function(col){
62722         return this.config[col].dataIndex;
62723     },
62724
62725     /**
62726      * Sets the dataIndex for a column.
62727      * @param {Number} col The column index
62728      * @param {Number} dataIndex The new dataIndex
62729      */
62730     setDataIndex : function(col, dataIndex){
62731         this.config[col].dataIndex = dataIndex;
62732     },
62733
62734     
62735     
62736     /**
62737      * Returns true if the cell is editable.
62738      * @param {Number} colIndex The column index
62739      * @param {Number} rowIndex The row index - this is nto actually used..?
62740      * @return {Boolean}
62741      */
62742     isCellEditable : function(colIndex, rowIndex){
62743         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
62744     },
62745
62746     /**
62747      * Returns the editor defined for the cell/column.
62748      * return false or null to disable editing.
62749      * @param {Number} colIndex The column index
62750      * @param {Number} rowIndex The row index
62751      * @return {Object}
62752      */
62753     getCellEditor : function(colIndex, rowIndex){
62754         return this.config[colIndex].editor;
62755     },
62756
62757     /**
62758      * Sets if a column is editable.
62759      * @param {Number} col The column index
62760      * @param {Boolean} editable True if the column is editable
62761      */
62762     setEditable : function(col, editable){
62763         this.config[col].editable = editable;
62764     },
62765
62766
62767     /**
62768      * Returns true if the column is hidden.
62769      * @param {Number} colIndex The column index
62770      * @return {Boolean}
62771      */
62772     isHidden : function(colIndex){
62773         return this.config[colIndex].hidden;
62774     },
62775
62776
62777     /**
62778      * Returns true if the column width cannot be changed
62779      */
62780     isFixed : function(colIndex){
62781         return this.config[colIndex].fixed;
62782     },
62783
62784     /**
62785      * Returns true if the column can be resized
62786      * @return {Boolean}
62787      */
62788     isResizable : function(colIndex){
62789         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
62790     },
62791     /**
62792      * Sets if a column is hidden.
62793      * @param {Number} colIndex The column index
62794      * @param {Boolean} hidden True if the column is hidden
62795      */
62796     setHidden : function(colIndex, hidden){
62797         this.config[colIndex].hidden = hidden;
62798         this.totalWidth = null;
62799         this.fireEvent("hiddenchange", this, colIndex, hidden);
62800     },
62801
62802     /**
62803      * Sets the editor for a column.
62804      * @param {Number} col The column index
62805      * @param {Object} editor The editor object
62806      */
62807     setEditor : function(col, editor){
62808         this.config[col].editor = editor;
62809     },
62810     /**
62811      * Add a column (experimental...) - defaults to adding to the end..
62812      * @param {Object} config 
62813     */
62814     addColumn : function(c)
62815     {
62816     
62817         var i = this.config.length;
62818         this.config[i] = c;
62819         
62820         if(typeof c.dataIndex == "undefined"){
62821             c.dataIndex = i;
62822         }
62823         if(typeof c.renderer == "string"){
62824             c.renderer = Roo.util.Format[c.renderer];
62825         }
62826         if(typeof c.id == "undefined"){
62827             c.id = Roo.id();
62828         }
62829         if(c.editor && c.editor.xtype){
62830             c.editor  = Roo.factory(c.editor, Roo.grid);
62831         }
62832         if(c.editor && c.editor.isFormField){
62833             c.editor = new Roo.grid.GridEditor(c.editor);
62834         }
62835         this.lookup[c.id] = c;
62836     }
62837     
62838 });
62839
62840 Roo.grid.ColumnModel.defaultRenderer = function(value)
62841 {
62842     if(typeof value == "object") {
62843         return value;
62844     }
62845         if(typeof value == "string" && value.length < 1){
62846             return "&#160;";
62847         }
62848     
62849         return String.format("{0}", value);
62850 };
62851
62852 // Alias for backwards compatibility
62853 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
62854 /*
62855  * Based on:
62856  * Ext JS Library 1.1.1
62857  * Copyright(c) 2006-2007, Ext JS, LLC.
62858  *
62859  * Originally Released Under LGPL - original licence link has changed is not relivant.
62860  *
62861  * Fork - LGPL
62862  * <script type="text/javascript">
62863  */
62864
62865 /**
62866  * @class Roo.grid.AbstractSelectionModel
62867  * @extends Roo.util.Observable
62868  * @abstract
62869  * Abstract base class for grid SelectionModels.  It provides the interface that should be
62870  * implemented by descendant classes.  This class should not be directly instantiated.
62871  * @constructor
62872  */
62873 Roo.grid.AbstractSelectionModel = function(){
62874     this.locked = false;
62875     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
62876 };
62877
62878 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
62879     /** @ignore Called by the grid automatically. Do not call directly. */
62880     init : function(grid){
62881         this.grid = grid;
62882         this.initEvents();
62883     },
62884
62885     /**
62886      * Locks the selections.
62887      */
62888     lock : function(){
62889         this.locked = true;
62890     },
62891
62892     /**
62893      * Unlocks the selections.
62894      */
62895     unlock : function(){
62896         this.locked = false;
62897     },
62898
62899     /**
62900      * Returns true if the selections are locked.
62901      * @return {Boolean}
62902      */
62903     isLocked : function(){
62904         return this.locked;
62905     }
62906 });/*
62907  * Based on:
62908  * Ext JS Library 1.1.1
62909  * Copyright(c) 2006-2007, Ext JS, LLC.
62910  *
62911  * Originally Released Under LGPL - original licence link has changed is not relivant.
62912  *
62913  * Fork - LGPL
62914  * <script type="text/javascript">
62915  */
62916 /**
62917  * @extends Roo.grid.AbstractSelectionModel
62918  * @class Roo.grid.RowSelectionModel
62919  * The default SelectionModel used by {@link Roo.grid.Grid}.
62920  * It supports multiple selections and keyboard selection/navigation. 
62921  * @constructor
62922  * @param {Object} config
62923  */
62924 Roo.grid.RowSelectionModel = function(config){
62925     Roo.apply(this, config);
62926     this.selections = new Roo.util.MixedCollection(false, function(o){
62927         return o.id;
62928     });
62929
62930     this.last = false;
62931     this.lastActive = false;
62932
62933     this.addEvents({
62934         /**
62935         * @event selectionchange
62936         * Fires when the selection changes
62937         * @param {SelectionModel} this
62938         */
62939        "selectionchange" : true,
62940        /**
62941         * @event afterselectionchange
62942         * Fires after the selection changes (eg. by key press or clicking)
62943         * @param {SelectionModel} this
62944         */
62945        "afterselectionchange" : true,
62946        /**
62947         * @event beforerowselect
62948         * Fires when a row is selected being selected, return false to cancel.
62949         * @param {SelectionModel} this
62950         * @param {Number} rowIndex The selected index
62951         * @param {Boolean} keepExisting False if other selections will be cleared
62952         */
62953        "beforerowselect" : true,
62954        /**
62955         * @event rowselect
62956         * Fires when a row is selected.
62957         * @param {SelectionModel} this
62958         * @param {Number} rowIndex The selected index
62959         * @param {Roo.data.Record} r The record
62960         */
62961        "rowselect" : true,
62962        /**
62963         * @event rowdeselect
62964         * Fires when a row is deselected.
62965         * @param {SelectionModel} this
62966         * @param {Number} rowIndex The selected index
62967         */
62968         "rowdeselect" : true
62969     });
62970     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
62971     this.locked = false;
62972 };
62973
62974 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
62975     /**
62976      * @cfg {Boolean} singleSelect
62977      * True to allow selection of only one row at a time (defaults to false)
62978      */
62979     singleSelect : false,
62980
62981     // private
62982     initEvents : function(){
62983
62984         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
62985             this.grid.on("mousedown", this.handleMouseDown, this);
62986         }else{ // allow click to work like normal
62987             this.grid.on("rowclick", this.handleDragableRowClick, this);
62988         }
62989         // bootstrap does not have a view..
62990         var view = this.grid.view ? this.grid.view : this.grid;
62991         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
62992             "up" : function(e){
62993                 if(!e.shiftKey){
62994                     this.selectPrevious(e.shiftKey);
62995                 }else if(this.last !== false && this.lastActive !== false){
62996                     var last = this.last;
62997                     this.selectRange(this.last,  this.lastActive-1);
62998                     view.focusRow(this.lastActive);
62999                     if(last !== false){
63000                         this.last = last;
63001                     }
63002                 }else{
63003                     this.selectFirstRow();
63004                 }
63005                 this.fireEvent("afterselectionchange", this);
63006             },
63007             "down" : function(e){
63008                 if(!e.shiftKey){
63009                     this.selectNext(e.shiftKey);
63010                 }else if(this.last !== false && this.lastActive !== false){
63011                     var last = this.last;
63012                     this.selectRange(this.last,  this.lastActive+1);
63013                     view.focusRow(this.lastActive);
63014                     if(last !== false){
63015                         this.last = last;
63016                     }
63017                 }else{
63018                     this.selectFirstRow();
63019                 }
63020                 this.fireEvent("afterselectionchange", this);
63021             },
63022             scope: this
63023         });
63024
63025          
63026         view.on("refresh", this.onRefresh, this);
63027         view.on("rowupdated", this.onRowUpdated, this);
63028         view.on("rowremoved", this.onRemove, this);
63029     },
63030
63031     // private
63032     onRefresh : function(){
63033         var ds = this.grid.ds, i, v = this.grid.view;
63034         var s = this.selections;
63035         s.each(function(r){
63036             if((i = ds.indexOfId(r.id)) != -1){
63037                 v.onRowSelect(i);
63038                 s.add(ds.getAt(i)); // updating the selection relate data
63039             }else{
63040                 s.remove(r);
63041             }
63042         });
63043     },
63044
63045     // private
63046     onRemove : function(v, index, r){
63047         this.selections.remove(r);
63048     },
63049
63050     // private
63051     onRowUpdated : function(v, index, r){
63052         if(this.isSelected(r)){
63053             v.onRowSelect(index);
63054         }
63055     },
63056
63057     /**
63058      * Select records.
63059      * @param {Array} records The records to select
63060      * @param {Boolean} keepExisting (optional) True to keep existing selections
63061      */
63062     selectRecords : function(records, keepExisting){
63063         if(!keepExisting){
63064             this.clearSelections();
63065         }
63066         var ds = this.grid.ds;
63067         for(var i = 0, len = records.length; i < len; i++){
63068             this.selectRow(ds.indexOf(records[i]), true);
63069         }
63070     },
63071
63072     /**
63073      * Gets the number of selected rows.
63074      * @return {Number}
63075      */
63076     getCount : function(){
63077         return this.selections.length;
63078     },
63079
63080     /**
63081      * Selects the first row in the grid.
63082      */
63083     selectFirstRow : function(){
63084         this.selectRow(0);
63085     },
63086
63087     /**
63088      * Select the last row.
63089      * @param {Boolean} keepExisting (optional) True to keep existing selections
63090      */
63091     selectLastRow : function(keepExisting){
63092         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
63093     },
63094
63095     /**
63096      * Selects the row immediately following the last selected row.
63097      * @param {Boolean} keepExisting (optional) True to keep existing selections
63098      */
63099     selectNext : function(keepExisting){
63100         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
63101             this.selectRow(this.last+1, keepExisting);
63102             var view = this.grid.view ? this.grid.view : this.grid;
63103             view.focusRow(this.last);
63104         }
63105     },
63106
63107     /**
63108      * Selects the row that precedes the last selected row.
63109      * @param {Boolean} keepExisting (optional) True to keep existing selections
63110      */
63111     selectPrevious : function(keepExisting){
63112         if(this.last){
63113             this.selectRow(this.last-1, keepExisting);
63114             var view = this.grid.view ? this.grid.view : this.grid;
63115             view.focusRow(this.last);
63116         }
63117     },
63118
63119     /**
63120      * Returns the selected records
63121      * @return {Array} Array of selected records
63122      */
63123     getSelections : function(){
63124         return [].concat(this.selections.items);
63125     },
63126
63127     /**
63128      * Returns the first selected record.
63129      * @return {Record}
63130      */
63131     getSelected : function(){
63132         return this.selections.itemAt(0);
63133     },
63134
63135
63136     /**
63137      * Clears all selections.
63138      */
63139     clearSelections : function(fast){
63140         if(this.locked) {
63141             return;
63142         }
63143         if(fast !== true){
63144             var ds = this.grid.ds;
63145             var s = this.selections;
63146             s.each(function(r){
63147                 this.deselectRow(ds.indexOfId(r.id));
63148             }, this);
63149             s.clear();
63150         }else{
63151             this.selections.clear();
63152         }
63153         this.last = false;
63154     },
63155
63156
63157     /**
63158      * Selects all rows.
63159      */
63160     selectAll : function(){
63161         if(this.locked) {
63162             return;
63163         }
63164         this.selections.clear();
63165         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
63166             this.selectRow(i, true);
63167         }
63168     },
63169
63170     /**
63171      * Returns True if there is a selection.
63172      * @return {Boolean}
63173      */
63174     hasSelection : function(){
63175         return this.selections.length > 0;
63176     },
63177
63178     /**
63179      * Returns True if the specified row is selected.
63180      * @param {Number/Record} record The record or index of the record to check
63181      * @return {Boolean}
63182      */
63183     isSelected : function(index){
63184         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
63185         return (r && this.selections.key(r.id) ? true : false);
63186     },
63187
63188     /**
63189      * Returns True if the specified record id is selected.
63190      * @param {String} id The id of record to check
63191      * @return {Boolean}
63192      */
63193     isIdSelected : function(id){
63194         return (this.selections.key(id) ? true : false);
63195     },
63196
63197     // private
63198     handleMouseDown : function(e, t)
63199     {
63200         var view = this.grid.view ? this.grid.view : this.grid;
63201         var rowIndex;
63202         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
63203             return;
63204         };
63205         if(e.shiftKey && this.last !== false){
63206             var last = this.last;
63207             this.selectRange(last, rowIndex, e.ctrlKey);
63208             this.last = last; // reset the last
63209             view.focusRow(rowIndex);
63210         }else{
63211             var isSelected = this.isSelected(rowIndex);
63212             if(e.button !== 0 && isSelected){
63213                 view.focusRow(rowIndex);
63214             }else if(e.ctrlKey && isSelected){
63215                 this.deselectRow(rowIndex);
63216             }else if(!isSelected){
63217                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
63218                 view.focusRow(rowIndex);
63219             }
63220         }
63221         this.fireEvent("afterselectionchange", this);
63222     },
63223     // private
63224     handleDragableRowClick :  function(grid, rowIndex, e) 
63225     {
63226         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
63227             this.selectRow(rowIndex, false);
63228             var view = this.grid.view ? this.grid.view : this.grid;
63229             view.focusRow(rowIndex);
63230              this.fireEvent("afterselectionchange", this);
63231         }
63232     },
63233     
63234     /**
63235      * Selects multiple rows.
63236      * @param {Array} rows Array of the indexes of the row to select
63237      * @param {Boolean} keepExisting (optional) True to keep existing selections
63238      */
63239     selectRows : function(rows, keepExisting){
63240         if(!keepExisting){
63241             this.clearSelections();
63242         }
63243         for(var i = 0, len = rows.length; i < len; i++){
63244             this.selectRow(rows[i], true);
63245         }
63246     },
63247
63248     /**
63249      * Selects a range of rows. All rows in between startRow and endRow are also selected.
63250      * @param {Number} startRow The index of the first row in the range
63251      * @param {Number} endRow The index of the last row in the range
63252      * @param {Boolean} keepExisting (optional) True to retain existing selections
63253      */
63254     selectRange : function(startRow, endRow, keepExisting){
63255         if(this.locked) {
63256             return;
63257         }
63258         if(!keepExisting){
63259             this.clearSelections();
63260         }
63261         if(startRow <= endRow){
63262             for(var i = startRow; i <= endRow; i++){
63263                 this.selectRow(i, true);
63264             }
63265         }else{
63266             for(var i = startRow; i >= endRow; i--){
63267                 this.selectRow(i, true);
63268             }
63269         }
63270     },
63271
63272     /**
63273      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
63274      * @param {Number} startRow The index of the first row in the range
63275      * @param {Number} endRow The index of the last row in the range
63276      */
63277     deselectRange : function(startRow, endRow, preventViewNotify){
63278         if(this.locked) {
63279             return;
63280         }
63281         for(var i = startRow; i <= endRow; i++){
63282             this.deselectRow(i, preventViewNotify);
63283         }
63284     },
63285
63286     /**
63287      * Selects a row.
63288      * @param {Number} row The index of the row to select
63289      * @param {Boolean} keepExisting (optional) True to keep existing selections
63290      */
63291     selectRow : function(index, keepExisting, preventViewNotify){
63292         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
63293             return;
63294         }
63295         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
63296             if(!keepExisting || this.singleSelect){
63297                 this.clearSelections();
63298             }
63299             var r = this.grid.ds.getAt(index);
63300             this.selections.add(r);
63301             this.last = this.lastActive = index;
63302             if(!preventViewNotify){
63303                 var view = this.grid.view ? this.grid.view : this.grid;
63304                 view.onRowSelect(index);
63305             }
63306             this.fireEvent("rowselect", this, index, r);
63307             this.fireEvent("selectionchange", this);
63308         }
63309     },
63310
63311     /**
63312      * Deselects a row.
63313      * @param {Number} row The index of the row to deselect
63314      */
63315     deselectRow : function(index, preventViewNotify){
63316         if(this.locked) {
63317             return;
63318         }
63319         if(this.last == index){
63320             this.last = false;
63321         }
63322         if(this.lastActive == index){
63323             this.lastActive = false;
63324         }
63325         var r = this.grid.ds.getAt(index);
63326         this.selections.remove(r);
63327         if(!preventViewNotify){
63328             var view = this.grid.view ? this.grid.view : this.grid;
63329             view.onRowDeselect(index);
63330         }
63331         this.fireEvent("rowdeselect", this, index);
63332         this.fireEvent("selectionchange", this);
63333     },
63334
63335     // private
63336     restoreLast : function(){
63337         if(this._last){
63338             this.last = this._last;
63339         }
63340     },
63341
63342     // private
63343     acceptsNav : function(row, col, cm){
63344         return !cm.isHidden(col) && cm.isCellEditable(col, row);
63345     },
63346
63347     // private
63348     onEditorKey : function(field, e){
63349         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
63350         if(k == e.TAB){
63351             e.stopEvent();
63352             ed.completeEdit();
63353             if(e.shiftKey){
63354                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
63355             }else{
63356                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
63357             }
63358         }else if(k == e.ENTER && !e.ctrlKey){
63359             e.stopEvent();
63360             ed.completeEdit();
63361             if(e.shiftKey){
63362                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
63363             }else{
63364                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
63365             }
63366         }else if(k == e.ESC){
63367             ed.cancelEdit();
63368         }
63369         if(newCell){
63370             g.startEditing(newCell[0], newCell[1]);
63371         }
63372     }
63373 });/*
63374  * Based on:
63375  * Ext JS Library 1.1.1
63376  * Copyright(c) 2006-2007, Ext JS, LLC.
63377  *
63378  * Originally Released Under LGPL - original licence link has changed is not relivant.
63379  *
63380  * Fork - LGPL
63381  * <script type="text/javascript">
63382  */
63383 /**
63384  * @class Roo.grid.CellSelectionModel
63385  * @extends Roo.grid.AbstractSelectionModel
63386  * This class provides the basic implementation for cell selection in a grid.
63387  * @constructor
63388  * @param {Object} config The object containing the configuration of this model.
63389  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
63390  */
63391 Roo.grid.CellSelectionModel = function(config){
63392     Roo.apply(this, config);
63393
63394     this.selection = null;
63395
63396     this.addEvents({
63397         /**
63398              * @event beforerowselect
63399              * Fires before a cell is selected.
63400              * @param {SelectionModel} this
63401              * @param {Number} rowIndex The selected row index
63402              * @param {Number} colIndex The selected cell index
63403              */
63404             "beforecellselect" : true,
63405         /**
63406              * @event cellselect
63407              * Fires when a cell is selected.
63408              * @param {SelectionModel} this
63409              * @param {Number} rowIndex The selected row index
63410              * @param {Number} colIndex The selected cell index
63411              */
63412             "cellselect" : true,
63413         /**
63414              * @event selectionchange
63415              * Fires when the active selection changes.
63416              * @param {SelectionModel} this
63417              * @param {Object} selection null for no selection or an object (o) with two properties
63418                 <ul>
63419                 <li>o.record: the record object for the row the selection is in</li>
63420                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
63421                 </ul>
63422              */
63423             "selectionchange" : true,
63424         /**
63425              * @event tabend
63426              * Fires when the tab (or enter) was pressed on the last editable cell
63427              * You can use this to trigger add new row.
63428              * @param {SelectionModel} this
63429              */
63430             "tabend" : true,
63431          /**
63432              * @event beforeeditnext
63433              * Fires before the next editable sell is made active
63434              * You can use this to skip to another cell or fire the tabend
63435              *    if you set cell to false
63436              * @param {Object} eventdata object : { cell : [ row, col ] } 
63437              */
63438             "beforeeditnext" : true
63439     });
63440     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
63441 };
63442
63443 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
63444     
63445     enter_is_tab: false,
63446
63447     /** @ignore */
63448     initEvents : function(){
63449         this.grid.on("mousedown", this.handleMouseDown, this);
63450         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
63451         var view = this.grid.view;
63452         view.on("refresh", this.onViewChange, this);
63453         view.on("rowupdated", this.onRowUpdated, this);
63454         view.on("beforerowremoved", this.clearSelections, this);
63455         view.on("beforerowsinserted", this.clearSelections, this);
63456         if(this.grid.isEditor){
63457             this.grid.on("beforeedit", this.beforeEdit,  this);
63458         }
63459     },
63460
63461         //private
63462     beforeEdit : function(e){
63463         this.select(e.row, e.column, false, true, e.record);
63464     },
63465
63466         //private
63467     onRowUpdated : function(v, index, r){
63468         if(this.selection && this.selection.record == r){
63469             v.onCellSelect(index, this.selection.cell[1]);
63470         }
63471     },
63472
63473         //private
63474     onViewChange : function(){
63475         this.clearSelections(true);
63476     },
63477
63478         /**
63479          * Returns the currently selected cell,.
63480          * @return {Array} The selected cell (row, column) or null if none selected.
63481          */
63482     getSelectedCell : function(){
63483         return this.selection ? this.selection.cell : null;
63484     },
63485
63486     /**
63487      * Clears all selections.
63488      * @param {Boolean} true to prevent the gridview from being notified about the change.
63489      */
63490     clearSelections : function(preventNotify){
63491         var s = this.selection;
63492         if(s){
63493             if(preventNotify !== true){
63494                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
63495             }
63496             this.selection = null;
63497             this.fireEvent("selectionchange", this, null);
63498         }
63499     },
63500
63501     /**
63502      * Returns true if there is a selection.
63503      * @return {Boolean}
63504      */
63505     hasSelection : function(){
63506         return this.selection ? true : false;
63507     },
63508
63509     /** @ignore */
63510     handleMouseDown : function(e, t){
63511         var v = this.grid.getView();
63512         if(this.isLocked()){
63513             return;
63514         };
63515         var row = v.findRowIndex(t);
63516         var cell = v.findCellIndex(t);
63517         if(row !== false && cell !== false){
63518             this.select(row, cell);
63519         }
63520     },
63521
63522     /**
63523      * Selects a cell.
63524      * @param {Number} rowIndex
63525      * @param {Number} collIndex
63526      */
63527     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
63528         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
63529             this.clearSelections();
63530             r = r || this.grid.dataSource.getAt(rowIndex);
63531             this.selection = {
63532                 record : r,
63533                 cell : [rowIndex, colIndex]
63534             };
63535             if(!preventViewNotify){
63536                 var v = this.grid.getView();
63537                 v.onCellSelect(rowIndex, colIndex);
63538                 if(preventFocus !== true){
63539                     v.focusCell(rowIndex, colIndex);
63540                 }
63541             }
63542             this.fireEvent("cellselect", this, rowIndex, colIndex);
63543             this.fireEvent("selectionchange", this, this.selection);
63544         }
63545     },
63546
63547         //private
63548     isSelectable : function(rowIndex, colIndex, cm){
63549         return !cm.isHidden(colIndex);
63550     },
63551
63552     /** @ignore */
63553     handleKeyDown : function(e){
63554         //Roo.log('Cell Sel Model handleKeyDown');
63555         if(!e.isNavKeyPress()){
63556             return;
63557         }
63558         var g = this.grid, s = this.selection;
63559         if(!s){
63560             e.stopEvent();
63561             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
63562             if(cell){
63563                 this.select(cell[0], cell[1]);
63564             }
63565             return;
63566         }
63567         var sm = this;
63568         var walk = function(row, col, step){
63569             return g.walkCells(row, col, step, sm.isSelectable,  sm);
63570         };
63571         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
63572         var newCell;
63573
63574       
63575
63576         switch(k){
63577             case e.TAB:
63578                 // handled by onEditorKey
63579                 if (g.isEditor && g.editing) {
63580                     return;
63581                 }
63582                 if(e.shiftKey) {
63583                     newCell = walk(r, c-1, -1);
63584                 } else {
63585                     newCell = walk(r, c+1, 1);
63586                 }
63587                 break;
63588             
63589             case e.DOWN:
63590                newCell = walk(r+1, c, 1);
63591                 break;
63592             
63593             case e.UP:
63594                 newCell = walk(r-1, c, -1);
63595                 break;
63596             
63597             case e.RIGHT:
63598                 newCell = walk(r, c+1, 1);
63599                 break;
63600             
63601             case e.LEFT:
63602                 newCell = walk(r, c-1, -1);
63603                 break;
63604             
63605             case e.ENTER:
63606                 
63607                 if(g.isEditor && !g.editing){
63608                    g.startEditing(r, c);
63609                    e.stopEvent();
63610                    return;
63611                 }
63612                 
63613                 
63614              break;
63615         };
63616         if(newCell){
63617             this.select(newCell[0], newCell[1]);
63618             e.stopEvent();
63619             
63620         }
63621     },
63622
63623     acceptsNav : function(row, col, cm){
63624         return !cm.isHidden(col) && cm.isCellEditable(col, row);
63625     },
63626     /**
63627      * Selects a cell.
63628      * @param {Number} field (not used) - as it's normally used as a listener
63629      * @param {Number} e - event - fake it by using
63630      *
63631      * var e = Roo.EventObjectImpl.prototype;
63632      * e.keyCode = e.TAB
63633      *
63634      * 
63635      */
63636     onEditorKey : function(field, e){
63637         
63638         var k = e.getKey(),
63639             newCell,
63640             g = this.grid,
63641             ed = g.activeEditor,
63642             forward = false;
63643         ///Roo.log('onEditorKey' + k);
63644         
63645         
63646         if (this.enter_is_tab && k == e.ENTER) {
63647             k = e.TAB;
63648         }
63649         
63650         if(k == e.TAB){
63651             if(e.shiftKey){
63652                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
63653             }else{
63654                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
63655                 forward = true;
63656             }
63657             
63658             e.stopEvent();
63659             
63660         } else if(k == e.ENTER &&  !e.ctrlKey){
63661             ed.completeEdit();
63662             e.stopEvent();
63663             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
63664         
63665                 } else if(k == e.ESC){
63666             ed.cancelEdit();
63667         }
63668                 
63669         if (newCell) {
63670             var ecall = { cell : newCell, forward : forward };
63671             this.fireEvent('beforeeditnext', ecall );
63672             newCell = ecall.cell;
63673                         forward = ecall.forward;
63674         }
63675                 
63676         if(newCell){
63677             //Roo.log('next cell after edit');
63678             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
63679         } else if (forward) {
63680             // tabbed past last
63681             this.fireEvent.defer(100, this, ['tabend',this]);
63682         }
63683     }
63684 });/*
63685  * Based on:
63686  * Ext JS Library 1.1.1
63687  * Copyright(c) 2006-2007, Ext JS, LLC.
63688  *
63689  * Originally Released Under LGPL - original licence link has changed is not relivant.
63690  *
63691  * Fork - LGPL
63692  * <script type="text/javascript">
63693  */
63694  
63695 /**
63696  * @class Roo.grid.EditorGrid
63697  * @extends Roo.grid.Grid
63698  * Class for creating and editable grid.
63699  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
63700  * The container MUST have some type of size defined for the grid to fill. The container will be 
63701  * automatically set to position relative if it isn't already.
63702  * @param {Object} dataSource The data model to bind to
63703  * @param {Object} colModel The column model with info about this grid's columns
63704  */
63705 Roo.grid.EditorGrid = function(container, config){
63706     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
63707     this.getGridEl().addClass("xedit-grid");
63708
63709     if(!this.selModel){
63710         this.selModel = new Roo.grid.CellSelectionModel();
63711     }
63712
63713     this.activeEditor = null;
63714
63715         this.addEvents({
63716             /**
63717              * @event beforeedit
63718              * Fires before cell editing is triggered. The edit event object has the following properties <br />
63719              * <ul style="padding:5px;padding-left:16px;">
63720              * <li>grid - This grid</li>
63721              * <li>record - The record being edited</li>
63722              * <li>field - The field name being edited</li>
63723              * <li>value - The value for the field being edited.</li>
63724              * <li>row - The grid row index</li>
63725              * <li>column - The grid column index</li>
63726              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
63727              * </ul>
63728              * @param {Object} e An edit event (see above for description)
63729              */
63730             "beforeedit" : true,
63731             /**
63732              * @event afteredit
63733              * Fires after a cell is edited. <br />
63734              * <ul style="padding:5px;padding-left:16px;">
63735              * <li>grid - This grid</li>
63736              * <li>record - The record being edited</li>
63737              * <li>field - The field name being edited</li>
63738              * <li>value - The value being set</li>
63739              * <li>originalValue - The original value for the field, before the edit.</li>
63740              * <li>row - The grid row index</li>
63741              * <li>column - The grid column index</li>
63742              * </ul>
63743              * @param {Object} e An edit event (see above for description)
63744              */
63745             "afteredit" : true,
63746             /**
63747              * @event validateedit
63748              * Fires after a cell is edited, but before the value is set in the record. 
63749          * You can use this to modify the value being set in the field, Return false
63750              * to cancel the change. The edit event object has the following properties <br />
63751              * <ul style="padding:5px;padding-left:16px;">
63752          * <li>editor - This editor</li>
63753              * <li>grid - This grid</li>
63754              * <li>record - The record being edited</li>
63755              * <li>field - The field name being edited</li>
63756              * <li>value - The value being set</li>
63757              * <li>originalValue - The original value for the field, before the edit.</li>
63758              * <li>row - The grid row index</li>
63759              * <li>column - The grid column index</li>
63760              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
63761              * </ul>
63762              * @param {Object} e An edit event (see above for description)
63763              */
63764             "validateedit" : true
63765         });
63766     this.on("bodyscroll", this.stopEditing,  this);
63767     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
63768 };
63769
63770 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
63771     /**
63772      * @cfg {Number} clicksToEdit
63773      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
63774      */
63775     clicksToEdit: 2,
63776
63777     // private
63778     isEditor : true,
63779     // private
63780     trackMouseOver: false, // causes very odd FF errors
63781
63782     onCellDblClick : function(g, row, col){
63783         this.startEditing(row, col);
63784     },
63785
63786     onEditComplete : function(ed, value, startValue){
63787         this.editing = false;
63788         this.activeEditor = null;
63789         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
63790         var r = ed.record;
63791         var field = this.colModel.getDataIndex(ed.col);
63792         var e = {
63793             grid: this,
63794             record: r,
63795             field: field,
63796             originalValue: startValue,
63797             value: value,
63798             row: ed.row,
63799             column: ed.col,
63800             cancel:false,
63801             editor: ed
63802         };
63803         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
63804         cell.show();
63805           
63806         if(String(value) !== String(startValue)){
63807             
63808             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
63809                 r.set(field, e.value);
63810                 // if we are dealing with a combo box..
63811                 // then we also set the 'name' colum to be the displayField
63812                 if (ed.field.displayField && ed.field.name) {
63813                     r.set(ed.field.name, ed.field.el.dom.value);
63814                 }
63815                 
63816                 delete e.cancel; //?? why!!!
63817                 this.fireEvent("afteredit", e);
63818             }
63819         } else {
63820             this.fireEvent("afteredit", e); // always fire it!
63821         }
63822         this.view.focusCell(ed.row, ed.col);
63823     },
63824
63825     /**
63826      * Starts editing the specified for the specified row/column
63827      * @param {Number} rowIndex
63828      * @param {Number} colIndex
63829      */
63830     startEditing : function(row, col){
63831         this.stopEditing();
63832         if(this.colModel.isCellEditable(col, row)){
63833             this.view.ensureVisible(row, col, true);
63834           
63835             var r = this.dataSource.getAt(row);
63836             var field = this.colModel.getDataIndex(col);
63837             var cell = Roo.get(this.view.getCell(row,col));
63838             var e = {
63839                 grid: this,
63840                 record: r,
63841                 field: field,
63842                 value: r.data[field],
63843                 row: row,
63844                 column: col,
63845                 cancel:false 
63846             };
63847             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
63848                 this.editing = true;
63849                 var ed = this.colModel.getCellEditor(col, row);
63850                 
63851                 if (!ed) {
63852                     return;
63853                 }
63854                 if(!ed.rendered){
63855                     ed.render(ed.parentEl || document.body);
63856                 }
63857                 ed.field.reset();
63858                
63859                 cell.hide();
63860                 
63861                 (function(){ // complex but required for focus issues in safari, ie and opera
63862                     ed.row = row;
63863                     ed.col = col;
63864                     ed.record = r;
63865                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
63866                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
63867                     this.activeEditor = ed;
63868                     var v = r.data[field];
63869                     ed.startEdit(this.view.getCell(row, col), v);
63870                     // combo's with 'displayField and name set
63871                     if (ed.field.displayField && ed.field.name) {
63872                         ed.field.el.dom.value = r.data[ed.field.name];
63873                     }
63874                     
63875                     
63876                 }).defer(50, this);
63877             }
63878         }
63879     },
63880         
63881     /**
63882      * Stops any active editing
63883      */
63884     stopEditing : function(){
63885         if(this.activeEditor){
63886             this.activeEditor.completeEdit();
63887         }
63888         this.activeEditor = null;
63889     },
63890         
63891          /**
63892      * Called to get grid's drag proxy text, by default returns this.ddText.
63893      * @return {String}
63894      */
63895     getDragDropText : function(){
63896         var count = this.selModel.getSelectedCell() ? 1 : 0;
63897         return String.format(this.ddText, count, count == 1 ? '' : 's');
63898     }
63899         
63900 });/*
63901  * Based on:
63902  * Ext JS Library 1.1.1
63903  * Copyright(c) 2006-2007, Ext JS, LLC.
63904  *
63905  * Originally Released Under LGPL - original licence link has changed is not relivant.
63906  *
63907  * Fork - LGPL
63908  * <script type="text/javascript">
63909  */
63910
63911 // private - not really -- you end up using it !
63912 // This is a support class used internally by the Grid components
63913
63914 /**
63915  * @class Roo.grid.GridEditor
63916  * @extends Roo.Editor
63917  * Class for creating and editable grid elements.
63918  * @param {Object} config any settings (must include field)
63919  */
63920 Roo.grid.GridEditor = function(field, config){
63921     if (!config && field.field) {
63922         config = field;
63923         field = Roo.factory(config.field, Roo.form);
63924     }
63925     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
63926     field.monitorTab = false;
63927 };
63928
63929 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
63930     
63931     /**
63932      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
63933      */
63934     
63935     alignment: "tl-tl",
63936     autoSize: "width",
63937     hideEl : false,
63938     cls: "x-small-editor x-grid-editor",
63939     shim:false,
63940     shadow:"frame"
63941 });/*
63942  * Based on:
63943  * Ext JS Library 1.1.1
63944  * Copyright(c) 2006-2007, Ext JS, LLC.
63945  *
63946  * Originally Released Under LGPL - original licence link has changed is not relivant.
63947  *
63948  * Fork - LGPL
63949  * <script type="text/javascript">
63950  */
63951   
63952
63953   
63954 Roo.grid.PropertyRecord = Roo.data.Record.create([
63955     {name:'name',type:'string'},  'value'
63956 ]);
63957
63958
63959 Roo.grid.PropertyStore = function(grid, source){
63960     this.grid = grid;
63961     this.store = new Roo.data.Store({
63962         recordType : Roo.grid.PropertyRecord
63963     });
63964     this.store.on('update', this.onUpdate,  this);
63965     if(source){
63966         this.setSource(source);
63967     }
63968     Roo.grid.PropertyStore.superclass.constructor.call(this);
63969 };
63970
63971
63972
63973 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
63974     setSource : function(o){
63975         this.source = o;
63976         this.store.removeAll();
63977         var data = [];
63978         for(var k in o){
63979             if(this.isEditableValue(o[k])){
63980                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
63981             }
63982         }
63983         this.store.loadRecords({records: data}, {}, true);
63984     },
63985
63986     onUpdate : function(ds, record, type){
63987         if(type == Roo.data.Record.EDIT){
63988             var v = record.data['value'];
63989             var oldValue = record.modified['value'];
63990             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
63991                 this.source[record.id] = v;
63992                 record.commit();
63993                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
63994             }else{
63995                 record.reject();
63996             }
63997         }
63998     },
63999
64000     getProperty : function(row){
64001        return this.store.getAt(row);
64002     },
64003
64004     isEditableValue: function(val){
64005         if(val && val instanceof Date){
64006             return true;
64007         }else if(typeof val == 'object' || typeof val == 'function'){
64008             return false;
64009         }
64010         return true;
64011     },
64012
64013     setValue : function(prop, value){
64014         this.source[prop] = value;
64015         this.store.getById(prop).set('value', value);
64016     },
64017
64018     getSource : function(){
64019         return this.source;
64020     }
64021 });
64022
64023 Roo.grid.PropertyColumnModel = function(grid, store){
64024     this.grid = grid;
64025     var g = Roo.grid;
64026     g.PropertyColumnModel.superclass.constructor.call(this, [
64027         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
64028         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
64029     ]);
64030     this.store = store;
64031     this.bselect = Roo.DomHelper.append(document.body, {
64032         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
64033             {tag: 'option', value: 'true', html: 'true'},
64034             {tag: 'option', value: 'false', html: 'false'}
64035         ]
64036     });
64037     Roo.id(this.bselect);
64038     var f = Roo.form;
64039     this.editors = {
64040         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
64041         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
64042         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
64043         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
64044         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
64045     };
64046     this.renderCellDelegate = this.renderCell.createDelegate(this);
64047     this.renderPropDelegate = this.renderProp.createDelegate(this);
64048 };
64049
64050 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
64051     
64052     
64053     nameText : 'Name',
64054     valueText : 'Value',
64055     
64056     dateFormat : 'm/j/Y',
64057     
64058     
64059     renderDate : function(dateVal){
64060         return dateVal.dateFormat(this.dateFormat);
64061     },
64062
64063     renderBool : function(bVal){
64064         return bVal ? 'true' : 'false';
64065     },
64066
64067     isCellEditable : function(colIndex, rowIndex){
64068         return colIndex == 1;
64069     },
64070
64071     getRenderer : function(col){
64072         return col == 1 ?
64073             this.renderCellDelegate : this.renderPropDelegate;
64074     },
64075
64076     renderProp : function(v){
64077         return this.getPropertyName(v);
64078     },
64079
64080     renderCell : function(val){
64081         var rv = val;
64082         if(val instanceof Date){
64083             rv = this.renderDate(val);
64084         }else if(typeof val == 'boolean'){
64085             rv = this.renderBool(val);
64086         }
64087         return Roo.util.Format.htmlEncode(rv);
64088     },
64089
64090     getPropertyName : function(name){
64091         var pn = this.grid.propertyNames;
64092         return pn && pn[name] ? pn[name] : name;
64093     },
64094
64095     getCellEditor : function(colIndex, rowIndex){
64096         var p = this.store.getProperty(rowIndex);
64097         var n = p.data['name'], val = p.data['value'];
64098         
64099         if(typeof(this.grid.customEditors[n]) == 'string'){
64100             return this.editors[this.grid.customEditors[n]];
64101         }
64102         if(typeof(this.grid.customEditors[n]) != 'undefined'){
64103             return this.grid.customEditors[n];
64104         }
64105         if(val instanceof Date){
64106             return this.editors['date'];
64107         }else if(typeof val == 'number'){
64108             return this.editors['number'];
64109         }else if(typeof val == 'boolean'){
64110             return this.editors['boolean'];
64111         }else{
64112             return this.editors['string'];
64113         }
64114     }
64115 });
64116
64117 /**
64118  * @class Roo.grid.PropertyGrid
64119  * @extends Roo.grid.EditorGrid
64120  * This class represents the  interface of a component based property grid control.
64121  * <br><br>Usage:<pre><code>
64122  var grid = new Roo.grid.PropertyGrid("my-container-id", {
64123       
64124  });
64125  // set any options
64126  grid.render();
64127  * </code></pre>
64128   
64129  * @constructor
64130  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
64131  * The container MUST have some type of size defined for the grid to fill. The container will be
64132  * automatically set to position relative if it isn't already.
64133  * @param {Object} config A config object that sets properties on this grid.
64134  */
64135 Roo.grid.PropertyGrid = function(container, config){
64136     config = config || {};
64137     var store = new Roo.grid.PropertyStore(this);
64138     this.store = store;
64139     var cm = new Roo.grid.PropertyColumnModel(this, store);
64140     store.store.sort('name', 'ASC');
64141     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
64142         ds: store.store,
64143         cm: cm,
64144         enableColLock:false,
64145         enableColumnMove:false,
64146         stripeRows:false,
64147         trackMouseOver: false,
64148         clicksToEdit:1
64149     }, config));
64150     this.getGridEl().addClass('x-props-grid');
64151     this.lastEditRow = null;
64152     this.on('columnresize', this.onColumnResize, this);
64153     this.addEvents({
64154          /**
64155              * @event beforepropertychange
64156              * Fires before a property changes (return false to stop?)
64157              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
64158              * @param {String} id Record Id
64159              * @param {String} newval New Value
64160          * @param {String} oldval Old Value
64161              */
64162         "beforepropertychange": true,
64163         /**
64164              * @event propertychange
64165              * Fires after a property changes
64166              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
64167              * @param {String} id Record Id
64168              * @param {String} newval New Value
64169          * @param {String} oldval Old Value
64170              */
64171         "propertychange": true
64172     });
64173     this.customEditors = this.customEditors || {};
64174 };
64175 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
64176     
64177      /**
64178      * @cfg {Object} customEditors map of colnames=> custom editors.
64179      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
64180      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
64181      * false disables editing of the field.
64182          */
64183     
64184       /**
64185      * @cfg {Object} propertyNames map of property Names to their displayed value
64186          */
64187     
64188     render : function(){
64189         Roo.grid.PropertyGrid.superclass.render.call(this);
64190         this.autoSize.defer(100, this);
64191     },
64192
64193     autoSize : function(){
64194         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
64195         if(this.view){
64196             this.view.fitColumns();
64197         }
64198     },
64199
64200     onColumnResize : function(){
64201         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
64202         this.autoSize();
64203     },
64204     /**
64205      * Sets the data for the Grid
64206      * accepts a Key => Value object of all the elements avaiable.
64207      * @param {Object} data  to appear in grid.
64208      */
64209     setSource : function(source){
64210         this.store.setSource(source);
64211         //this.autoSize();
64212     },
64213     /**
64214      * Gets all the data from the grid.
64215      * @return {Object} data  data stored in grid
64216      */
64217     getSource : function(){
64218         return this.store.getSource();
64219     }
64220 });/*
64221   
64222  * Licence LGPL
64223  
64224  */
64225  
64226 /**
64227  * @class Roo.grid.Calendar
64228  * @extends Roo.grid.Grid
64229  * This class extends the Grid to provide a calendar widget
64230  * <br><br>Usage:<pre><code>
64231  var grid = new Roo.grid.Calendar("my-container-id", {
64232      ds: myDataStore,
64233      cm: myColModel,
64234      selModel: mySelectionModel,
64235      autoSizeColumns: true,
64236      monitorWindowResize: false,
64237      trackMouseOver: true
64238      eventstore : real data store..
64239  });
64240  // set any options
64241  grid.render();
64242   
64243   * @constructor
64244  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
64245  * The container MUST have some type of size defined for the grid to fill. The container will be
64246  * automatically set to position relative if it isn't already.
64247  * @param {Object} config A config object that sets properties on this grid.
64248  */
64249 Roo.grid.Calendar = function(container, config){
64250         // initialize the container
64251         this.container = Roo.get(container);
64252         this.container.update("");
64253         this.container.setStyle("overflow", "hidden");
64254     this.container.addClass('x-grid-container');
64255
64256     this.id = this.container.id;
64257
64258     Roo.apply(this, config);
64259     // check and correct shorthanded configs
64260     
64261     var rows = [];
64262     var d =1;
64263     for (var r = 0;r < 6;r++) {
64264         
64265         rows[r]=[];
64266         for (var c =0;c < 7;c++) {
64267             rows[r][c]= '';
64268         }
64269     }
64270     if (this.eventStore) {
64271         this.eventStore= Roo.factory(this.eventStore, Roo.data);
64272         this.eventStore.on('load',this.onLoad, this);
64273         this.eventStore.on('beforeload',this.clearEvents, this);
64274          
64275     }
64276     
64277     this.dataSource = new Roo.data.Store({
64278             proxy: new Roo.data.MemoryProxy(rows),
64279             reader: new Roo.data.ArrayReader({}, [
64280                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
64281     });
64282
64283     this.dataSource.load();
64284     this.ds = this.dataSource;
64285     this.ds.xmodule = this.xmodule || false;
64286     
64287     
64288     var cellRender = function(v,x,r)
64289     {
64290         return String.format(
64291             '<div class="fc-day  fc-widget-content"><div>' +
64292                 '<div class="fc-event-container"></div>' +
64293                 '<div class="fc-day-number">{0}</div>'+
64294                 
64295                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
64296             '</div></div>', v);
64297     
64298     }
64299     
64300     
64301     this.colModel = new Roo.grid.ColumnModel( [
64302         {
64303             xtype: 'ColumnModel',
64304             xns: Roo.grid,
64305             dataIndex : 'weekday0',
64306             header : 'Sunday',
64307             renderer : cellRender
64308         },
64309         {
64310             xtype: 'ColumnModel',
64311             xns: Roo.grid,
64312             dataIndex : 'weekday1',
64313             header : 'Monday',
64314             renderer : cellRender
64315         },
64316         {
64317             xtype: 'ColumnModel',
64318             xns: Roo.grid,
64319             dataIndex : 'weekday2',
64320             header : 'Tuesday',
64321             renderer : cellRender
64322         },
64323         {
64324             xtype: 'ColumnModel',
64325             xns: Roo.grid,
64326             dataIndex : 'weekday3',
64327             header : 'Wednesday',
64328             renderer : cellRender
64329         },
64330         {
64331             xtype: 'ColumnModel',
64332             xns: Roo.grid,
64333             dataIndex : 'weekday4',
64334             header : 'Thursday',
64335             renderer : cellRender
64336         },
64337         {
64338             xtype: 'ColumnModel',
64339             xns: Roo.grid,
64340             dataIndex : 'weekday5',
64341             header : 'Friday',
64342             renderer : cellRender
64343         },
64344         {
64345             xtype: 'ColumnModel',
64346             xns: Roo.grid,
64347             dataIndex : 'weekday6',
64348             header : 'Saturday',
64349             renderer : cellRender
64350         }
64351     ]);
64352     this.cm = this.colModel;
64353     this.cm.xmodule = this.xmodule || false;
64354  
64355         
64356           
64357     //this.selModel = new Roo.grid.CellSelectionModel();
64358     //this.sm = this.selModel;
64359     //this.selModel.init(this);
64360     
64361     
64362     if(this.width){
64363         this.container.setWidth(this.width);
64364     }
64365
64366     if(this.height){
64367         this.container.setHeight(this.height);
64368     }
64369     /** @private */
64370         this.addEvents({
64371         // raw events
64372         /**
64373          * @event click
64374          * The raw click event for the entire grid.
64375          * @param {Roo.EventObject} e
64376          */
64377         "click" : true,
64378         /**
64379          * @event dblclick
64380          * The raw dblclick event for the entire grid.
64381          * @param {Roo.EventObject} e
64382          */
64383         "dblclick" : true,
64384         /**
64385          * @event contextmenu
64386          * The raw contextmenu event for the entire grid.
64387          * @param {Roo.EventObject} e
64388          */
64389         "contextmenu" : true,
64390         /**
64391          * @event mousedown
64392          * The raw mousedown event for the entire grid.
64393          * @param {Roo.EventObject} e
64394          */
64395         "mousedown" : true,
64396         /**
64397          * @event mouseup
64398          * The raw mouseup event for the entire grid.
64399          * @param {Roo.EventObject} e
64400          */
64401         "mouseup" : true,
64402         /**
64403          * @event mouseover
64404          * The raw mouseover event for the entire grid.
64405          * @param {Roo.EventObject} e
64406          */
64407         "mouseover" : true,
64408         /**
64409          * @event mouseout
64410          * The raw mouseout event for the entire grid.
64411          * @param {Roo.EventObject} e
64412          */
64413         "mouseout" : true,
64414         /**
64415          * @event keypress
64416          * The raw keypress event for the entire grid.
64417          * @param {Roo.EventObject} e
64418          */
64419         "keypress" : true,
64420         /**
64421          * @event keydown
64422          * The raw keydown event for the entire grid.
64423          * @param {Roo.EventObject} e
64424          */
64425         "keydown" : true,
64426
64427         // custom events
64428
64429         /**
64430          * @event cellclick
64431          * Fires when a cell is clicked
64432          * @param {Grid} this
64433          * @param {Number} rowIndex
64434          * @param {Number} columnIndex
64435          * @param {Roo.EventObject} e
64436          */
64437         "cellclick" : true,
64438         /**
64439          * @event celldblclick
64440          * Fires when a cell is double clicked
64441          * @param {Grid} this
64442          * @param {Number} rowIndex
64443          * @param {Number} columnIndex
64444          * @param {Roo.EventObject} e
64445          */
64446         "celldblclick" : true,
64447         /**
64448          * @event rowclick
64449          * Fires when a row is clicked
64450          * @param {Grid} this
64451          * @param {Number} rowIndex
64452          * @param {Roo.EventObject} e
64453          */
64454         "rowclick" : true,
64455         /**
64456          * @event rowdblclick
64457          * Fires when a row is double clicked
64458          * @param {Grid} this
64459          * @param {Number} rowIndex
64460          * @param {Roo.EventObject} e
64461          */
64462         "rowdblclick" : true,
64463         /**
64464          * @event headerclick
64465          * Fires when a header is clicked
64466          * @param {Grid} this
64467          * @param {Number} columnIndex
64468          * @param {Roo.EventObject} e
64469          */
64470         "headerclick" : true,
64471         /**
64472          * @event headerdblclick
64473          * Fires when a header cell is double clicked
64474          * @param {Grid} this
64475          * @param {Number} columnIndex
64476          * @param {Roo.EventObject} e
64477          */
64478         "headerdblclick" : true,
64479         /**
64480          * @event rowcontextmenu
64481          * Fires when a row is right clicked
64482          * @param {Grid} this
64483          * @param {Number} rowIndex
64484          * @param {Roo.EventObject} e
64485          */
64486         "rowcontextmenu" : true,
64487         /**
64488          * @event cellcontextmenu
64489          * Fires when a cell is right clicked
64490          * @param {Grid} this
64491          * @param {Number} rowIndex
64492          * @param {Number} cellIndex
64493          * @param {Roo.EventObject} e
64494          */
64495          "cellcontextmenu" : true,
64496         /**
64497          * @event headercontextmenu
64498          * Fires when a header is right clicked
64499          * @param {Grid} this
64500          * @param {Number} columnIndex
64501          * @param {Roo.EventObject} e
64502          */
64503         "headercontextmenu" : true,
64504         /**
64505          * @event bodyscroll
64506          * Fires when the body element is scrolled
64507          * @param {Number} scrollLeft
64508          * @param {Number} scrollTop
64509          */
64510         "bodyscroll" : true,
64511         /**
64512          * @event columnresize
64513          * Fires when the user resizes a column
64514          * @param {Number} columnIndex
64515          * @param {Number} newSize
64516          */
64517         "columnresize" : true,
64518         /**
64519          * @event columnmove
64520          * Fires when the user moves a column
64521          * @param {Number} oldIndex
64522          * @param {Number} newIndex
64523          */
64524         "columnmove" : true,
64525         /**
64526          * @event startdrag
64527          * Fires when row(s) start being dragged
64528          * @param {Grid} this
64529          * @param {Roo.GridDD} dd The drag drop object
64530          * @param {event} e The raw browser event
64531          */
64532         "startdrag" : true,
64533         /**
64534          * @event enddrag
64535          * Fires when a drag operation is complete
64536          * @param {Grid} this
64537          * @param {Roo.GridDD} dd The drag drop object
64538          * @param {event} e The raw browser event
64539          */
64540         "enddrag" : true,
64541         /**
64542          * @event dragdrop
64543          * Fires when dragged row(s) are dropped on a valid DD target
64544          * @param {Grid} this
64545          * @param {Roo.GridDD} dd The drag drop object
64546          * @param {String} targetId The target drag drop object
64547          * @param {event} e The raw browser event
64548          */
64549         "dragdrop" : true,
64550         /**
64551          * @event dragover
64552          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
64553          * @param {Grid} this
64554          * @param {Roo.GridDD} dd The drag drop object
64555          * @param {String} targetId The target drag drop object
64556          * @param {event} e The raw browser event
64557          */
64558         "dragover" : true,
64559         /**
64560          * @event dragenter
64561          *  Fires when the dragged row(s) first cross another DD target while being dragged
64562          * @param {Grid} this
64563          * @param {Roo.GridDD} dd The drag drop object
64564          * @param {String} targetId The target drag drop object
64565          * @param {event} e The raw browser event
64566          */
64567         "dragenter" : true,
64568         /**
64569          * @event dragout
64570          * Fires when the dragged row(s) leave another DD target while being dragged
64571          * @param {Grid} this
64572          * @param {Roo.GridDD} dd The drag drop object
64573          * @param {String} targetId The target drag drop object
64574          * @param {event} e The raw browser event
64575          */
64576         "dragout" : true,
64577         /**
64578          * @event rowclass
64579          * Fires when a row is rendered, so you can change add a style to it.
64580          * @param {GridView} gridview   The grid view
64581          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
64582          */
64583         'rowclass' : true,
64584
64585         /**
64586          * @event render
64587          * Fires when the grid is rendered
64588          * @param {Grid} grid
64589          */
64590         'render' : true,
64591             /**
64592              * @event select
64593              * Fires when a date is selected
64594              * @param {DatePicker} this
64595              * @param {Date} date The selected date
64596              */
64597         'select': true,
64598         /**
64599              * @event monthchange
64600              * Fires when the displayed month changes 
64601              * @param {DatePicker} this
64602              * @param {Date} date The selected month
64603              */
64604         'monthchange': true,
64605         /**
64606              * @event evententer
64607              * Fires when mouse over an event
64608              * @param {Calendar} this
64609              * @param {event} Event
64610              */
64611         'evententer': true,
64612         /**
64613              * @event eventleave
64614              * Fires when the mouse leaves an
64615              * @param {Calendar} this
64616              * @param {event}
64617              */
64618         'eventleave': true,
64619         /**
64620              * @event eventclick
64621              * Fires when the mouse click an
64622              * @param {Calendar} this
64623              * @param {event}
64624              */
64625         'eventclick': true,
64626         /**
64627              * @event eventrender
64628              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
64629              * @param {Calendar} this
64630              * @param {data} data to be modified
64631              */
64632         'eventrender': true
64633         
64634     });
64635
64636     Roo.grid.Grid.superclass.constructor.call(this);
64637     this.on('render', function() {
64638         this.view.el.addClass('x-grid-cal'); 
64639         
64640         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
64641
64642     },this);
64643     
64644     if (!Roo.grid.Calendar.style) {
64645         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
64646             
64647             
64648             '.x-grid-cal .x-grid-col' :  {
64649                 height: 'auto !important',
64650                 'vertical-align': 'top'
64651             },
64652             '.x-grid-cal  .fc-event-hori' : {
64653                 height: '14px'
64654             }
64655              
64656             
64657         }, Roo.id());
64658     }
64659
64660     
64661     
64662 };
64663 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
64664     /**
64665      * @cfg {Store} eventStore The store that loads events.
64666      */
64667     eventStore : 25,
64668
64669      
64670     activeDate : false,
64671     startDay : 0,
64672     autoWidth : true,
64673     monitorWindowResize : false,
64674
64675     
64676     resizeColumns : function() {
64677         var col = (this.view.el.getWidth() / 7) - 3;
64678         // loop through cols, and setWidth
64679         for(var i =0 ; i < 7 ; i++){
64680             this.cm.setColumnWidth(i, col);
64681         }
64682     },
64683      setDate :function(date) {
64684         
64685         Roo.log('setDate?');
64686         
64687         this.resizeColumns();
64688         var vd = this.activeDate;
64689         this.activeDate = date;
64690 //        if(vd && this.el){
64691 //            var t = date.getTime();
64692 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
64693 //                Roo.log('using add remove');
64694 //                
64695 //                this.fireEvent('monthchange', this, date);
64696 //                
64697 //                this.cells.removeClass("fc-state-highlight");
64698 //                this.cells.each(function(c){
64699 //                   if(c.dateValue == t){
64700 //                       c.addClass("fc-state-highlight");
64701 //                       setTimeout(function(){
64702 //                            try{c.dom.firstChild.focus();}catch(e){}
64703 //                       }, 50);
64704 //                       return false;
64705 //                   }
64706 //                   return true;
64707 //                });
64708 //                return;
64709 //            }
64710 //        }
64711         
64712         var days = date.getDaysInMonth();
64713         
64714         var firstOfMonth = date.getFirstDateOfMonth();
64715         var startingPos = firstOfMonth.getDay()-this.startDay;
64716         
64717         if(startingPos < this.startDay){
64718             startingPos += 7;
64719         }
64720         
64721         var pm = date.add(Date.MONTH, -1);
64722         var prevStart = pm.getDaysInMonth()-startingPos;
64723 //        
64724         
64725         
64726         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
64727         
64728         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
64729         //this.cells.addClassOnOver('fc-state-hover');
64730         
64731         var cells = this.cells.elements;
64732         var textEls = this.textNodes;
64733         
64734         //Roo.each(cells, function(cell){
64735         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
64736         //});
64737         
64738         days += startingPos;
64739
64740         // convert everything to numbers so it's fast
64741         var day = 86400000;
64742         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
64743         //Roo.log(d);
64744         //Roo.log(pm);
64745         //Roo.log(prevStart);
64746         
64747         var today = new Date().clearTime().getTime();
64748         var sel = date.clearTime().getTime();
64749         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
64750         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
64751         var ddMatch = this.disabledDatesRE;
64752         var ddText = this.disabledDatesText;
64753         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
64754         var ddaysText = this.disabledDaysText;
64755         var format = this.format;
64756         
64757         var setCellClass = function(cal, cell){
64758             
64759             //Roo.log('set Cell Class');
64760             cell.title = "";
64761             var t = d.getTime();
64762             
64763             //Roo.log(d);
64764             
64765             
64766             cell.dateValue = t;
64767             if(t == today){
64768                 cell.className += " fc-today";
64769                 cell.className += " fc-state-highlight";
64770                 cell.title = cal.todayText;
64771             }
64772             if(t == sel){
64773                 // disable highlight in other month..
64774                 cell.className += " fc-state-highlight";
64775                 
64776             }
64777             // disabling
64778             if(t < min) {
64779                 //cell.className = " fc-state-disabled";
64780                 cell.title = cal.minText;
64781                 return;
64782             }
64783             if(t > max) {
64784                 //cell.className = " fc-state-disabled";
64785                 cell.title = cal.maxText;
64786                 return;
64787             }
64788             if(ddays){
64789                 if(ddays.indexOf(d.getDay()) != -1){
64790                     // cell.title = ddaysText;
64791                    // cell.className = " fc-state-disabled";
64792                 }
64793             }
64794             if(ddMatch && format){
64795                 var fvalue = d.dateFormat(format);
64796                 if(ddMatch.test(fvalue)){
64797                     cell.title = ddText.replace("%0", fvalue);
64798                    cell.className = " fc-state-disabled";
64799                 }
64800             }
64801             
64802             if (!cell.initialClassName) {
64803                 cell.initialClassName = cell.dom.className;
64804             }
64805             
64806             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
64807         };
64808
64809         var i = 0;
64810         
64811         for(; i < startingPos; i++) {
64812             cells[i].dayName =  (++prevStart);
64813             Roo.log(textEls[i]);
64814             d.setDate(d.getDate()+1);
64815             
64816             //cells[i].className = "fc-past fc-other-month";
64817             setCellClass(this, cells[i]);
64818         }
64819         
64820         var intDay = 0;
64821         
64822         for(; i < days; i++){
64823             intDay = i - startingPos + 1;
64824             cells[i].dayName =  (intDay);
64825             d.setDate(d.getDate()+1);
64826             
64827             cells[i].className = ''; // "x-date-active";
64828             setCellClass(this, cells[i]);
64829         }
64830         var extraDays = 0;
64831         
64832         for(; i < 42; i++) {
64833             //textEls[i].innerHTML = (++extraDays);
64834             
64835             d.setDate(d.getDate()+1);
64836             cells[i].dayName = (++extraDays);
64837             cells[i].className = "fc-future fc-other-month";
64838             setCellClass(this, cells[i]);
64839         }
64840         
64841         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
64842         
64843         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
64844         
64845         // this will cause all the cells to mis
64846         var rows= [];
64847         var i =0;
64848         for (var r = 0;r < 6;r++) {
64849             for (var c =0;c < 7;c++) {
64850                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
64851             }    
64852         }
64853         
64854         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
64855         for(i=0;i<cells.length;i++) {
64856             
64857             this.cells.elements[i].dayName = cells[i].dayName ;
64858             this.cells.elements[i].className = cells[i].className;
64859             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
64860             this.cells.elements[i].title = cells[i].title ;
64861             this.cells.elements[i].dateValue = cells[i].dateValue ;
64862         }
64863         
64864         
64865         
64866         
64867         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
64868         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
64869         
64870         ////if(totalRows != 6){
64871             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
64872            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
64873        // }
64874         
64875         this.fireEvent('monthchange', this, date);
64876         
64877         
64878     },
64879  /**
64880      * Returns the grid's SelectionModel.
64881      * @return {SelectionModel}
64882      */
64883     getSelectionModel : function(){
64884         if(!this.selModel){
64885             this.selModel = new Roo.grid.CellSelectionModel();
64886         }
64887         return this.selModel;
64888     },
64889
64890     load: function() {
64891         this.eventStore.load()
64892         
64893         
64894         
64895     },
64896     
64897     findCell : function(dt) {
64898         dt = dt.clearTime().getTime();
64899         var ret = false;
64900         this.cells.each(function(c){
64901             //Roo.log("check " +c.dateValue + '?=' + dt);
64902             if(c.dateValue == dt){
64903                 ret = c;
64904                 return false;
64905             }
64906             return true;
64907         });
64908         
64909         return ret;
64910     },
64911     
64912     findCells : function(rec) {
64913         var s = rec.data.start_dt.clone().clearTime().getTime();
64914        // Roo.log(s);
64915         var e= rec.data.end_dt.clone().clearTime().getTime();
64916        // Roo.log(e);
64917         var ret = [];
64918         this.cells.each(function(c){
64919              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
64920             
64921             if(c.dateValue > e){
64922                 return ;
64923             }
64924             if(c.dateValue < s){
64925                 return ;
64926             }
64927             ret.push(c);
64928         });
64929         
64930         return ret;    
64931     },
64932     
64933     findBestRow: function(cells)
64934     {
64935         var ret = 0;
64936         
64937         for (var i =0 ; i < cells.length;i++) {
64938             ret  = Math.max(cells[i].rows || 0,ret);
64939         }
64940         return ret;
64941         
64942     },
64943     
64944     
64945     addItem : function(rec)
64946     {
64947         // look for vertical location slot in
64948         var cells = this.findCells(rec);
64949         
64950         rec.row = this.findBestRow(cells);
64951         
64952         // work out the location.
64953         
64954         var crow = false;
64955         var rows = [];
64956         for(var i =0; i < cells.length; i++) {
64957             if (!crow) {
64958                 crow = {
64959                     start : cells[i],
64960                     end :  cells[i]
64961                 };
64962                 continue;
64963             }
64964             if (crow.start.getY() == cells[i].getY()) {
64965                 // on same row.
64966                 crow.end = cells[i];
64967                 continue;
64968             }
64969             // different row.
64970             rows.push(crow);
64971             crow = {
64972                 start: cells[i],
64973                 end : cells[i]
64974             };
64975             
64976         }
64977         
64978         rows.push(crow);
64979         rec.els = [];
64980         rec.rows = rows;
64981         rec.cells = cells;
64982         for (var i = 0; i < cells.length;i++) {
64983             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
64984             
64985         }
64986         
64987         
64988     },
64989     
64990     clearEvents: function() {
64991         
64992         if (!this.eventStore.getCount()) {
64993             return;
64994         }
64995         // reset number of rows in cells.
64996         Roo.each(this.cells.elements, function(c){
64997             c.rows = 0;
64998         });
64999         
65000         this.eventStore.each(function(e) {
65001             this.clearEvent(e);
65002         },this);
65003         
65004     },
65005     
65006     clearEvent : function(ev)
65007     {
65008         if (ev.els) {
65009             Roo.each(ev.els, function(el) {
65010                 el.un('mouseenter' ,this.onEventEnter, this);
65011                 el.un('mouseleave' ,this.onEventLeave, this);
65012                 el.remove();
65013             },this);
65014             ev.els = [];
65015         }
65016     },
65017     
65018     
65019     renderEvent : function(ev,ctr) {
65020         if (!ctr) {
65021              ctr = this.view.el.select('.fc-event-container',true).first();
65022         }
65023         
65024          
65025         this.clearEvent(ev);
65026             //code
65027        
65028         
65029         
65030         ev.els = [];
65031         var cells = ev.cells;
65032         var rows = ev.rows;
65033         this.fireEvent('eventrender', this, ev);
65034         
65035         for(var i =0; i < rows.length; i++) {
65036             
65037             cls = '';
65038             if (i == 0) {
65039                 cls += ' fc-event-start';
65040             }
65041             if ((i+1) == rows.length) {
65042                 cls += ' fc-event-end';
65043             }
65044             
65045             //Roo.log(ev.data);
65046             // how many rows should it span..
65047             var cg = this.eventTmpl.append(ctr,Roo.apply({
65048                 fccls : cls
65049                 
65050             }, ev.data) , true);
65051             
65052             
65053             cg.on('mouseenter' ,this.onEventEnter, this, ev);
65054             cg.on('mouseleave' ,this.onEventLeave, this, ev);
65055             cg.on('click', this.onEventClick, this, ev);
65056             
65057             ev.els.push(cg);
65058             
65059             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
65060             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
65061             //Roo.log(cg);
65062              
65063             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
65064             cg.setWidth(ebox.right - sbox.x -2);
65065         }
65066     },
65067     
65068     renderEvents: function()
65069     {   
65070         // first make sure there is enough space..
65071         
65072         if (!this.eventTmpl) {
65073             this.eventTmpl = new Roo.Template(
65074                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
65075                     '<div class="fc-event-inner">' +
65076                         '<span class="fc-event-time">{time}</span>' +
65077                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
65078                     '</div>' +
65079                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
65080                 '</div>'
65081             );
65082                 
65083         }
65084                
65085         
65086         
65087         this.cells.each(function(c) {
65088             //Roo.log(c.select('.fc-day-content div',true).first());
65089             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
65090         });
65091         
65092         var ctr = this.view.el.select('.fc-event-container',true).first();
65093         
65094         var cls;
65095         this.eventStore.each(function(ev){
65096             
65097             this.renderEvent(ev);
65098              
65099              
65100         }, this);
65101         this.view.layout();
65102         
65103     },
65104     
65105     onEventEnter: function (e, el,event,d) {
65106         this.fireEvent('evententer', this, el, event);
65107     },
65108     
65109     onEventLeave: function (e, el,event,d) {
65110         this.fireEvent('eventleave', this, el, event);
65111     },
65112     
65113     onEventClick: function (e, el,event,d) {
65114         this.fireEvent('eventclick', this, el, event);
65115     },
65116     
65117     onMonthChange: function () {
65118         this.store.load();
65119     },
65120     
65121     onLoad: function () {
65122         
65123         //Roo.log('calendar onload');
65124 //         
65125         if(this.eventStore.getCount() > 0){
65126             
65127            
65128             
65129             this.eventStore.each(function(d){
65130                 
65131                 
65132                 // FIXME..
65133                 var add =   d.data;
65134                 if (typeof(add.end_dt) == 'undefined')  {
65135                     Roo.log("Missing End time in calendar data: ");
65136                     Roo.log(d);
65137                     return;
65138                 }
65139                 if (typeof(add.start_dt) == 'undefined')  {
65140                     Roo.log("Missing Start time in calendar data: ");
65141                     Roo.log(d);
65142                     return;
65143                 }
65144                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
65145                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
65146                 add.id = add.id || d.id;
65147                 add.title = add.title || '??';
65148                 
65149                 this.addItem(d);
65150                 
65151              
65152             },this);
65153         }
65154         
65155         this.renderEvents();
65156     }
65157     
65158
65159 });
65160 /*
65161  grid : {
65162                 xtype: 'Grid',
65163                 xns: Roo.grid,
65164                 listeners : {
65165                     render : function ()
65166                     {
65167                         _this.grid = this;
65168                         
65169                         if (!this.view.el.hasClass('course-timesheet')) {
65170                             this.view.el.addClass('course-timesheet');
65171                         }
65172                         if (this.tsStyle) {
65173                             this.ds.load({});
65174                             return; 
65175                         }
65176                         Roo.log('width');
65177                         Roo.log(_this.grid.view.el.getWidth());
65178                         
65179                         
65180                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
65181                             '.course-timesheet .x-grid-row' : {
65182                                 height: '80px'
65183                             },
65184                             '.x-grid-row td' : {
65185                                 'vertical-align' : 0
65186                             },
65187                             '.course-edit-link' : {
65188                                 'color' : 'blue',
65189                                 'text-overflow' : 'ellipsis',
65190                                 'overflow' : 'hidden',
65191                                 'white-space' : 'nowrap',
65192                                 'cursor' : 'pointer'
65193                             },
65194                             '.sub-link' : {
65195                                 'color' : 'green'
65196                             },
65197                             '.de-act-sup-link' : {
65198                                 'color' : 'purple',
65199                                 'text-decoration' : 'line-through'
65200                             },
65201                             '.de-act-link' : {
65202                                 'color' : 'red',
65203                                 'text-decoration' : 'line-through'
65204                             },
65205                             '.course-timesheet .course-highlight' : {
65206                                 'border-top-style': 'dashed !important',
65207                                 'border-bottom-bottom': 'dashed !important'
65208                             },
65209                             '.course-timesheet .course-item' : {
65210                                 'font-family'   : 'tahoma, arial, helvetica',
65211                                 'font-size'     : '11px',
65212                                 'overflow'      : 'hidden',
65213                                 'padding-left'  : '10px',
65214                                 'padding-right' : '10px',
65215                                 'padding-top' : '10px' 
65216                             }
65217                             
65218                         }, Roo.id());
65219                                 this.ds.load({});
65220                     }
65221                 },
65222                 autoWidth : true,
65223                 monitorWindowResize : false,
65224                 cellrenderer : function(v,x,r)
65225                 {
65226                     return v;
65227                 },
65228                 sm : {
65229                     xtype: 'CellSelectionModel',
65230                     xns: Roo.grid
65231                 },
65232                 dataSource : {
65233                     xtype: 'Store',
65234                     xns: Roo.data,
65235                     listeners : {
65236                         beforeload : function (_self, options)
65237                         {
65238                             options.params = options.params || {};
65239                             options.params._month = _this.monthField.getValue();
65240                             options.params.limit = 9999;
65241                             options.params['sort'] = 'when_dt';    
65242                             options.params['dir'] = 'ASC';    
65243                             this.proxy.loadResponse = this.loadResponse;
65244                             Roo.log("load?");
65245                             //this.addColumns();
65246                         },
65247                         load : function (_self, records, options)
65248                         {
65249                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
65250                                 // if you click on the translation.. you can edit it...
65251                                 var el = Roo.get(this);
65252                                 var id = el.dom.getAttribute('data-id');
65253                                 var d = el.dom.getAttribute('data-date');
65254                                 var t = el.dom.getAttribute('data-time');
65255                                 //var id = this.child('span').dom.textContent;
65256                                 
65257                                 //Roo.log(this);
65258                                 Pman.Dialog.CourseCalendar.show({
65259                                     id : id,
65260                                     when_d : d,
65261                                     when_t : t,
65262                                     productitem_active : id ? 1 : 0
65263                                 }, function() {
65264                                     _this.grid.ds.load({});
65265                                 });
65266                            
65267                            });
65268                            
65269                            _this.panel.fireEvent('resize', [ '', '' ]);
65270                         }
65271                     },
65272                     loadResponse : function(o, success, response){
65273                             // this is overridden on before load..
65274                             
65275                             Roo.log("our code?");       
65276                             //Roo.log(success);
65277                             //Roo.log(response)
65278                             delete this.activeRequest;
65279                             if(!success){
65280                                 this.fireEvent("loadexception", this, o, response);
65281                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
65282                                 return;
65283                             }
65284                             var result;
65285                             try {
65286                                 result = o.reader.read(response);
65287                             }catch(e){
65288                                 Roo.log("load exception?");
65289                                 this.fireEvent("loadexception", this, o, response, e);
65290                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
65291                                 return;
65292                             }
65293                             Roo.log("ready...");        
65294                             // loop through result.records;
65295                             // and set this.tdate[date] = [] << array of records..
65296                             _this.tdata  = {};
65297                             Roo.each(result.records, function(r){
65298                                 //Roo.log(r.data);
65299                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
65300                                     _this.tdata[r.data.when_dt.format('j')] = [];
65301                                 }
65302                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
65303                             });
65304                             
65305                             //Roo.log(_this.tdata);
65306                             
65307                             result.records = [];
65308                             result.totalRecords = 6;
65309                     
65310                             // let's generate some duumy records for the rows.
65311                             //var st = _this.dateField.getValue();
65312                             
65313                             // work out monday..
65314                             //st = st.add(Date.DAY, -1 * st.format('w'));
65315                             
65316                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
65317                             
65318                             var firstOfMonth = date.getFirstDayOfMonth();
65319                             var days = date.getDaysInMonth();
65320                             var d = 1;
65321                             var firstAdded = false;
65322                             for (var i = 0; i < result.totalRecords ; i++) {
65323                                 //var d= st.add(Date.DAY, i);
65324                                 var row = {};
65325                                 var added = 0;
65326                                 for(var w = 0 ; w < 7 ; w++){
65327                                     if(!firstAdded && firstOfMonth != w){
65328                                         continue;
65329                                     }
65330                                     if(d > days){
65331                                         continue;
65332                                     }
65333                                     firstAdded = true;
65334                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
65335                                     row['weekday'+w] = String.format(
65336                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
65337                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
65338                                                     d,
65339                                                     date.format('Y-m-')+dd
65340                                                 );
65341                                     added++;
65342                                     if(typeof(_this.tdata[d]) != 'undefined'){
65343                                         Roo.each(_this.tdata[d], function(r){
65344                                             var is_sub = '';
65345                                             var deactive = '';
65346                                             var id = r.id;
65347                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
65348                                             if(r.parent_id*1>0){
65349                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
65350                                                 id = r.parent_id;
65351                                             }
65352                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
65353                                                 deactive = 'de-act-link';
65354                                             }
65355                                             
65356                                             row['weekday'+w] += String.format(
65357                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
65358                                                     id, //0
65359                                                     r.product_id_name, //1
65360                                                     r.when_dt.format('h:ia'), //2
65361                                                     is_sub, //3
65362                                                     deactive, //4
65363                                                     desc // 5
65364                                             );
65365                                         });
65366                                     }
65367                                     d++;
65368                                 }
65369                                 
65370                                 // only do this if something added..
65371                                 if(added > 0){ 
65372                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
65373                                 }
65374                                 
65375                                 
65376                                 // push it twice. (second one with an hour..
65377                                 
65378                             }
65379                             //Roo.log(result);
65380                             this.fireEvent("load", this, o, o.request.arg);
65381                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
65382                         },
65383                     sortInfo : {field: 'when_dt', direction : 'ASC' },
65384                     proxy : {
65385                         xtype: 'HttpProxy',
65386                         xns: Roo.data,
65387                         method : 'GET',
65388                         url : baseURL + '/Roo/Shop_course.php'
65389                     },
65390                     reader : {
65391                         xtype: 'JsonReader',
65392                         xns: Roo.data,
65393                         id : 'id',
65394                         fields : [
65395                             {
65396                                 'name': 'id',
65397                                 'type': 'int'
65398                             },
65399                             {
65400                                 'name': 'when_dt',
65401                                 'type': 'string'
65402                             },
65403                             {
65404                                 'name': 'end_dt',
65405                                 'type': 'string'
65406                             },
65407                             {
65408                                 'name': 'parent_id',
65409                                 'type': 'int'
65410                             },
65411                             {
65412                                 'name': 'product_id',
65413                                 'type': 'int'
65414                             },
65415                             {
65416                                 'name': 'productitem_id',
65417                                 'type': 'int'
65418                             },
65419                             {
65420                                 'name': 'guid',
65421                                 'type': 'int'
65422                             }
65423                         ]
65424                     }
65425                 },
65426                 toolbar : {
65427                     xtype: 'Toolbar',
65428                     xns: Roo,
65429                     items : [
65430                         {
65431                             xtype: 'Button',
65432                             xns: Roo.Toolbar,
65433                             listeners : {
65434                                 click : function (_self, e)
65435                                 {
65436                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
65437                                     sd.setMonth(sd.getMonth()-1);
65438                                     _this.monthField.setValue(sd.format('Y-m-d'));
65439                                     _this.grid.ds.load({});
65440                                 }
65441                             },
65442                             text : "Back"
65443                         },
65444                         {
65445                             xtype: 'Separator',
65446                             xns: Roo.Toolbar
65447                         },
65448                         {
65449                             xtype: 'MonthField',
65450                             xns: Roo.form,
65451                             listeners : {
65452                                 render : function (_self)
65453                                 {
65454                                     _this.monthField = _self;
65455                                    // _this.monthField.set  today
65456                                 },
65457                                 select : function (combo, date)
65458                                 {
65459                                     _this.grid.ds.load({});
65460                                 }
65461                             },
65462                             value : (function() { return new Date(); })()
65463                         },
65464                         {
65465                             xtype: 'Separator',
65466                             xns: Roo.Toolbar
65467                         },
65468                         {
65469                             xtype: 'TextItem',
65470                             xns: Roo.Toolbar,
65471                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
65472                         },
65473                         {
65474                             xtype: 'Fill',
65475                             xns: Roo.Toolbar
65476                         },
65477                         {
65478                             xtype: 'Button',
65479                             xns: Roo.Toolbar,
65480                             listeners : {
65481                                 click : function (_self, e)
65482                                 {
65483                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
65484                                     sd.setMonth(sd.getMonth()+1);
65485                                     _this.monthField.setValue(sd.format('Y-m-d'));
65486                                     _this.grid.ds.load({});
65487                                 }
65488                             },
65489                             text : "Next"
65490                         }
65491                     ]
65492                 },
65493                  
65494             }
65495         };
65496         
65497         *//*
65498  * Based on:
65499  * Ext JS Library 1.1.1
65500  * Copyright(c) 2006-2007, Ext JS, LLC.
65501  *
65502  * Originally Released Under LGPL - original licence link has changed is not relivant.
65503  *
65504  * Fork - LGPL
65505  * <script type="text/javascript">
65506  */
65507  
65508 /**
65509  * @class Roo.LoadMask
65510  * A simple utility class for generically masking elements while loading data.  If the element being masked has
65511  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
65512  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
65513  * element's UpdateManager load indicator and will be destroyed after the initial load.
65514  * @constructor
65515  * Create a new LoadMask
65516  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
65517  * @param {Object} config The config object
65518  */
65519 Roo.LoadMask = function(el, config){
65520     this.el = Roo.get(el);
65521     Roo.apply(this, config);
65522     if(this.store){
65523         this.store.on('beforeload', this.onBeforeLoad, this);
65524         this.store.on('load', this.onLoad, this);
65525         this.store.on('loadexception', this.onLoadException, this);
65526         this.removeMask = false;
65527     }else{
65528         var um = this.el.getUpdateManager();
65529         um.showLoadIndicator = false; // disable the default indicator
65530         um.on('beforeupdate', this.onBeforeLoad, this);
65531         um.on('update', this.onLoad, this);
65532         um.on('failure', this.onLoad, this);
65533         this.removeMask = true;
65534     }
65535 };
65536
65537 Roo.LoadMask.prototype = {
65538     /**
65539      * @cfg {Boolean} removeMask
65540      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
65541      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
65542      */
65543     removeMask : false,
65544     /**
65545      * @cfg {String} msg
65546      * The text to display in a centered loading message box (defaults to 'Loading...')
65547      */
65548     msg : 'Loading...',
65549     /**
65550      * @cfg {String} msgCls
65551      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
65552      */
65553     msgCls : 'x-mask-loading',
65554
65555     /**
65556      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
65557      * @type Boolean
65558      */
65559     disabled: false,
65560
65561     /**
65562      * Disables the mask to prevent it from being displayed
65563      */
65564     disable : function(){
65565        this.disabled = true;
65566     },
65567
65568     /**
65569      * Enables the mask so that it can be displayed
65570      */
65571     enable : function(){
65572         this.disabled = false;
65573     },
65574     
65575     onLoadException : function()
65576     {
65577         Roo.log(arguments);
65578         
65579         if (typeof(arguments[3]) != 'undefined') {
65580             Roo.MessageBox.alert("Error loading",arguments[3]);
65581         } 
65582         /*
65583         try {
65584             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
65585                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
65586             }   
65587         } catch(e) {
65588             
65589         }
65590         */
65591     
65592         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
65593     },
65594     // private
65595     onLoad : function()
65596     {
65597         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
65598     },
65599
65600     // private
65601     onBeforeLoad : function(){
65602         if(!this.disabled){
65603             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
65604         }
65605     },
65606
65607     // private
65608     destroy : function(){
65609         if(this.store){
65610             this.store.un('beforeload', this.onBeforeLoad, this);
65611             this.store.un('load', this.onLoad, this);
65612             this.store.un('loadexception', this.onLoadException, this);
65613         }else{
65614             var um = this.el.getUpdateManager();
65615             um.un('beforeupdate', this.onBeforeLoad, this);
65616             um.un('update', this.onLoad, this);
65617             um.un('failure', this.onLoad, this);
65618         }
65619     }
65620 };/*
65621  * Based on:
65622  * Ext JS Library 1.1.1
65623  * Copyright(c) 2006-2007, Ext JS, LLC.
65624  *
65625  * Originally Released Under LGPL - original licence link has changed is not relivant.
65626  *
65627  * Fork - LGPL
65628  * <script type="text/javascript">
65629  */
65630
65631
65632 /**
65633  * @class Roo.XTemplate
65634  * @extends Roo.Template
65635  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
65636 <pre><code>
65637 var t = new Roo.XTemplate(
65638         '&lt;select name="{name}"&gt;',
65639                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
65640         '&lt;/select&gt;'
65641 );
65642  
65643 // then append, applying the master template values
65644  </code></pre>
65645  *
65646  * Supported features:
65647  *
65648  *  Tags:
65649
65650 <pre><code>
65651       {a_variable} - output encoded.
65652       {a_variable.format:("Y-m-d")} - call a method on the variable
65653       {a_variable:raw} - unencoded output
65654       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
65655       {a_variable:this.method_on_template(...)} - call a method on the template object.
65656  
65657 </code></pre>
65658  *  The tpl tag:
65659 <pre><code>
65660         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
65661         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
65662         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
65663         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
65664   
65665         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
65666         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
65667 </code></pre>
65668  *      
65669  */
65670 Roo.XTemplate = function()
65671 {
65672     Roo.XTemplate.superclass.constructor.apply(this, arguments);
65673     if (this.html) {
65674         this.compile();
65675     }
65676 };
65677
65678
65679 Roo.extend(Roo.XTemplate, Roo.Template, {
65680
65681     /**
65682      * The various sub templates
65683      */
65684     tpls : false,
65685     /**
65686      *
65687      * basic tag replacing syntax
65688      * WORD:WORD()
65689      *
65690      * // you can fake an object call by doing this
65691      *  x.t:(test,tesT) 
65692      * 
65693      */
65694     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
65695
65696     /**
65697      * compile the template
65698      *
65699      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
65700      *
65701      */
65702     compile: function()
65703     {
65704         var s = this.html;
65705      
65706         s = ['<tpl>', s, '</tpl>'].join('');
65707     
65708         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
65709             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
65710             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
65711             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
65712             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
65713             m,
65714             id     = 0,
65715             tpls   = [];
65716     
65717         while(true == !!(m = s.match(re))){
65718             var forMatch   = m[0].match(nameRe),
65719                 ifMatch   = m[0].match(ifRe),
65720                 execMatch   = m[0].match(execRe),
65721                 namedMatch   = m[0].match(namedRe),
65722                 
65723                 exp  = null, 
65724                 fn   = null,
65725                 exec = null,
65726                 name = forMatch && forMatch[1] ? forMatch[1] : '';
65727                 
65728             if (ifMatch) {
65729                 // if - puts fn into test..
65730                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
65731                 if(exp){
65732                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
65733                 }
65734             }
65735             
65736             if (execMatch) {
65737                 // exec - calls a function... returns empty if true is  returned.
65738                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
65739                 if(exp){
65740                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
65741                 }
65742             }
65743             
65744             
65745             if (name) {
65746                 // for = 
65747                 switch(name){
65748                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
65749                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
65750                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
65751                 }
65752             }
65753             var uid = namedMatch ? namedMatch[1] : id;
65754             
65755             
65756             tpls.push({
65757                 id:     namedMatch ? namedMatch[1] : id,
65758                 target: name,
65759                 exec:   exec,
65760                 test:   fn,
65761                 body:   m[1] || ''
65762             });
65763             if (namedMatch) {
65764                 s = s.replace(m[0], '');
65765             } else { 
65766                 s = s.replace(m[0], '{xtpl'+ id + '}');
65767             }
65768             ++id;
65769         }
65770         this.tpls = [];
65771         for(var i = tpls.length-1; i >= 0; --i){
65772             this.compileTpl(tpls[i]);
65773             this.tpls[tpls[i].id] = tpls[i];
65774         }
65775         this.master = tpls[tpls.length-1];
65776         return this;
65777     },
65778     /**
65779      * same as applyTemplate, except it's done to one of the subTemplates
65780      * when using named templates, you can do:
65781      *
65782      * var str = pl.applySubTemplate('your-name', values);
65783      *
65784      * 
65785      * @param {Number} id of the template
65786      * @param {Object} values to apply to template
65787      * @param {Object} parent (normaly the instance of this object)
65788      */
65789     applySubTemplate : function(id, values, parent)
65790     {
65791         
65792         
65793         var t = this.tpls[id];
65794         
65795         
65796         try { 
65797             if(t.test && !t.test.call(this, values, parent)){
65798                 return '';
65799             }
65800         } catch(e) {
65801             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
65802             Roo.log(e.toString());
65803             Roo.log(t.test);
65804             return ''
65805         }
65806         try { 
65807             
65808             if(t.exec && t.exec.call(this, values, parent)){
65809                 return '';
65810             }
65811         } catch(e) {
65812             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
65813             Roo.log(e.toString());
65814             Roo.log(t.exec);
65815             return ''
65816         }
65817         try {
65818             var vs = t.target ? t.target.call(this, values, parent) : values;
65819             parent = t.target ? values : parent;
65820             if(t.target && vs instanceof Array){
65821                 var buf = [];
65822                 for(var i = 0, len = vs.length; i < len; i++){
65823                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
65824                 }
65825                 return buf.join('');
65826             }
65827             return t.compiled.call(this, vs, parent);
65828         } catch (e) {
65829             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
65830             Roo.log(e.toString());
65831             Roo.log(t.compiled);
65832             return '';
65833         }
65834     },
65835
65836     compileTpl : function(tpl)
65837     {
65838         var fm = Roo.util.Format;
65839         var useF = this.disableFormats !== true;
65840         var sep = Roo.isGecko ? "+" : ",";
65841         var undef = function(str) {
65842             Roo.log("Property not found :"  + str);
65843             return '';
65844         };
65845         
65846         var fn = function(m, name, format, args)
65847         {
65848             //Roo.log(arguments);
65849             args = args ? args.replace(/\\'/g,"'") : args;
65850             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
65851             if (typeof(format) == 'undefined') {
65852                 format= 'htmlEncode';
65853             }
65854             if (format == 'raw' ) {
65855                 format = false;
65856             }
65857             
65858             if(name.substr(0, 4) == 'xtpl'){
65859                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
65860             }
65861             
65862             // build an array of options to determine if value is undefined..
65863             
65864             // basically get 'xxxx.yyyy' then do
65865             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
65866             //    (function () { Roo.log("Property not found"); return ''; })() :
65867             //    ......
65868             
65869             var udef_ar = [];
65870             var lookfor = '';
65871             Roo.each(name.split('.'), function(st) {
65872                 lookfor += (lookfor.length ? '.': '') + st;
65873                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
65874             });
65875             
65876             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
65877             
65878             
65879             if(format && useF){
65880                 
65881                 args = args ? ',' + args : "";
65882                  
65883                 if(format.substr(0, 5) != "this."){
65884                     format = "fm." + format + '(';
65885                 }else{
65886                     format = 'this.call("'+ format.substr(5) + '", ';
65887                     args = ", values";
65888                 }
65889                 
65890                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
65891             }
65892              
65893             if (args.length) {
65894                 // called with xxyx.yuu:(test,test)
65895                 // change to ()
65896                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
65897             }
65898             // raw.. - :raw modifier..
65899             return "'"+ sep + udef_st  + name + ")"+sep+"'";
65900             
65901         };
65902         var body;
65903         // branched to use + in gecko and [].join() in others
65904         if(Roo.isGecko){
65905             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
65906                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
65907                     "';};};";
65908         }else{
65909             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
65910             body.push(tpl.body.replace(/(\r\n|\n)/g,
65911                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
65912             body.push("'].join('');};};");
65913             body = body.join('');
65914         }
65915         
65916         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
65917        
65918         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
65919         eval(body);
65920         
65921         return this;
65922     },
65923
65924     applyTemplate : function(values){
65925         return this.master.compiled.call(this, values, {});
65926         //var s = this.subs;
65927     },
65928
65929     apply : function(){
65930         return this.applyTemplate.apply(this, arguments);
65931     }
65932
65933  });
65934
65935 Roo.XTemplate.from = function(el){
65936     el = Roo.getDom(el);
65937     return new Roo.XTemplate(el.value || el.innerHTML);
65938 };